Commit Graph

12 Commits

Author SHA1 Message Date
Ryuta Kamizono 0adcec4954 PERF: Avoid extra delegation to `LazyAttributeHash`
The extra delegation to `LazyAttributeHash` has non-negligible overhead.

Avoiding that delegation makes attributes access about 45% faster for
readonly (non-mutation) usage.

https://gist.github.com/kamipo/4002c96a02859d8fe6503e26d7be4ad8

Before:

```
IPS
Warming up --------------------------------------
    attribute access     1.000  i/100ms
Calculating -------------------------------------
    attribute access      3.444  (± 0.0%) i/s -     18.000  in   5.259030s
MEMORY
Calculating -------------------------------------
    attribute access    38.902M memsize (     0.000  retained)
                       350.044k objects (     0.000  retained)
                        15.000  strings (     0.000  retained)
```

After (with `immutable_strings_by_default = true`):

```
IPS
Warming up --------------------------------------
    attribute access     1.000  i/100ms
Calculating -------------------------------------
    attribute access      5.066  (±19.7%) i/s -     25.000  in   5.024650s
MEMORY
Calculating -------------------------------------
    attribute access    27.382M memsize (     0.000  retained)
                       160.044k objects (     0.000  retained)
                        15.000  strings (     0.000  retained)
```
2020-06-15 09:26:24 +09:00
Ryuta Kamizono f93b04afab Deprecate marshalling load from legacy attributes format
Since #31827, marshalling attributes hash format is changed to improve
performance because materializing lazy attribute hash is too expensive.

In that time, we had kept an ability to load from legacy attributes
format, since that performance improvement is backported to 5-1-stable
and 5-0-stable.

Now all supported versions will dump attributes as new format, the
backward compatibity should no longer be needed.
2020-05-02 15:49:09 +09:00
Rafael Mendonça França 0e62667047
Revert "No such class since 8d2866bb80fbe81acb04f5b0c44f152f571fb29f"
This reverts commit dd779c9686.
2019-08-02 00:25:13 -04:00
Akira Matsuda dd779c9686 No such class since 8d2866bb80 2019-08-02 06:21:14 +09:00
Sharang Dashputre 3c4b729f48 Fix spellings for 'unmarshall(ing/ed)' & 'marshall(ing/ed)' 2018-10-02 13:55:39 +05:30
Ryuta Kamizono 8f2bb58ba2
PERF: Recover marshaling dump/load performance (#31827)
* PERF: Recover marshaling dump/load performance

This performance regression which is described in #30680 was caused by
f0ddf87 due to force materialized `LazyAttributeHash`.

Since 95b86e5, default proc has been removed in the class, so it is no
longer needed that force materialized.

Avoiding force materialized will recover marshaling dump/load
performance.

Benchmark:

https://gist.github.com/blimmer/1360ea51cd3147bae8aeb7c6d09bff17

Before:

```
it took 0.6248569069430232 seconds to unmarshal the objects

Total allocated: 38681544 bytes (530060 objects)

allocated memory by class
-----------------------------------
  12138848  Hash
  10542384  String
   7920000  ActiveModel::Attribute::Uninitialized
   5600000  ActiveModel::Attribute::FromDatabase
   1200000  Foo
    880000  ActiveModel::LazyAttributeHash
    400000  ActiveModel::AttributeSet
        80  Integer
        72  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
        40  ActiveModel::Type::String
        40  ActiveRecord::Type::DateTime
        40  Object
        40  Range

allocated objects by class
-----------------------------------
    250052  String
    110000  ActiveModel::Attribute::Uninitialized
     70001  Hash
     70000  ActiveModel::Attribute::FromDatabase
     10000  ActiveModel::AttributeSet
     10000  ActiveModel::LazyAttributeHash
     10000  Foo
         2  Integer
         1  ActiveModel::Type::String
         1  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
         1  ActiveRecord::Type::DateTime
         1  Object
         1  Range
```

After:

```
it took 0.1660824950085953 seconds to unmarshal the objects

Total allocated: 13883811 bytes (220090 objects)

allocated memory by class
-----------------------------------
   5743371  String
   4940008  Hash
   1200000  Foo
    880000  ActiveModel::LazyAttributeHash
    720000  Array
    400000  ActiveModel::AttributeSet
        80  ActiveModel::Attribute::FromDatabase
        80  Integer
        72  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
        40  ActiveModel::Type::String
        40  ActiveModel::Type::Value
        40  ActiveRecord::Type::DateTime
        40  Object
        40  Range

allocated objects by class
-----------------------------------
    130077  String
     50004  Hash
     10000  ActiveModel::AttributeSet
     10000  ActiveModel::LazyAttributeHash
     10000  Array
     10000  Foo
         2  Integer
         1  ActiveModel::Attribute::FromDatabase
         1  ActiveModel::Type::String
         1  ActiveModel::Type::Value
         1  ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer
         1  ActiveRecord::Type::DateTime
         1  Object
         1  Range
```

Fixes #30680.

* Keep the `@delegate_hash` to avoid to lose any mutations that have been made to the record
2018-02-02 07:52:33 +09:00
Ryuta Kamizono 393d497a53 Don't expose `attributes_with_uninitialized_key` utility method
It is not a test case.
2018-01-30 10:16:05 +09:00
Daniel Colson 94333a4c31 Use assert_predicate and assert_not_predicate 2018-01-25 23:32:59 -05:00
Sean Griffin 95b86e57a6 Change how `AttributeSet::Builder` receives its defaults
There are two concerns which are both being combined into one here, but
both have the same goal. There are certain attributes which we want to
always consider initialized. Previously, they were handled separately.
The primary key (which is assumed to be backed by a database column)
needs to be initialized, because there is a ton of code in Active Record
that assumes `foo.id` will never raise. Additionally, we want attributes
which aren't backed by a database column to always be initialized, since
we would never receive a database value for them.

Ultimately these two concerns can be combined into one. The old
implementation hid a lot of inherent complexity, and is hard to optimize
from the outside. We can simplify things significantly by just passing
in a hash.

This has slightly different semantics from the old behavior, in that
`Foo.select(:bar).first.id` will return the default value for the
primary key, rather than `nil` unconditionally -- however, the default
value is always `nil` in practice.
2017-11-27 14:06:51 -07:00
Ryuta Kamizono 24b59434e6
Add missing autoload `Type` (#31123)
Attribute modules (`Attribute`, `Attributes`, `AttributeSet`) uses
`Type`, but referencing `Type` before the modules still fail.

```
% ./bin/test -w test/cases/attribute_test.rb -n test_with_value_from_user_validates_the_value
Run options: -n test_with_value_from_user_validates_the_value --seed 31876

E

Error:
ActiveModel::AttributeTest#test_with_value_from_user_validates_the_value:
NameError: uninitialized constant ActiveModel::AttributeTest::Type
    /Users/kamipo/src/github.com/rails/rails/activemodel/test/cases/attribute_test.rb:233:in `block in <class:AttributeTest>'

bin/test test/cases/attribute_test.rb:232

Finished in 0.002985s, 335.0479 runs/s, 335.0479 assertions/s.
1 runs, 1 assertions, 0 failures, 1 errors, 0 skips
```

Probably we need more autoloading at least `Type`.
2017-11-11 06:43:54 +09:00
yuuji.yaginuma c59ab795ee Add missing requires
Currently, executing the test with only `attribute_set_test.rb` results in an error.

```
./bin/test -w test/cases/attribute_set_test.rb
Run options: --seed 33470

# Running:

E

Error:
ActiveModel::AttributeSetTest#test_#map_returns_a_new_attribute_set_with_the_changes_applied:
NameError: uninitialized constant ActiveModel::AttributeSetTest::AttributeSet
Did you mean?  ActiveModel::Attributes
               ActiveModel::Attribute
    activemodel/test/cases/attribute_set_test.rb:235:in `block in <class:AttributeSetTest>'

bin/test test/cases/attribute_set_test.rb:234
```

Added a missing require to fix this.

Also, I suspect that this is the cause of failures in CI.
Ref: https://travis-ci.org/rails/rails/jobs/299994708
2017-11-10 14:28:02 +09:00
Lisa Ugray c3675f50d2 Move Attribute and AttributeSet to ActiveModel
Use these to back the attributes API.  Stop automatically including
ActiveModel::Dirty in ActiveModel::Attributes, and make it optional.
2017-11-09 14:29:39 -05:00