Commit Graph

10 Commits

Author SHA1 Message Date
Ryuta Kamizono 5090a64aa3 Lazy allocate `@forced_changes`
It is almost no longer used in Active Record.
2020-06-14 13:50:07 +09:00
Ryuta Kamizono 6b0a9de906 PERF: 2x ~ 30x faster dirty tracking
Currently, although using both dirty tracking (ivar backed and
attributes backed) on one model is not supported (doesn't fully work at
least), both dirty tracking are being performed, that is very slow.

As long as attributes backed dirty tracking is used, ivar backed dirty
tracking should not need to be performed.

I've refactored to extract new `ForcedMutationTracker` which only tracks
`force_change` to be performed for ivar backed dirty tracking, that
makes dirty tracking on Active Record 2x ~ 30x faster.

https://gist.github.com/kamipo/971dfe0891f0fe1ec7db8ab31f016435

Before:

```
Warming up --------------------------------------
            changed?     4.467k i/100ms
             changed     5.134k i/100ms
             changes     3.023k i/100ms
  changed_attributes     4.358k i/100ms
        title_change     3.185k i/100ms
           title_was     3.381k i/100ms
Calculating -------------------------------------
            changed?     42.197k (±28.5%) i/s -    187.614k in   5.050446s
             changed     50.481k (±16.0%) i/s -    246.432k in   5.045759s
             changes     30.799k (± 7.2%) i/s -    154.173k in   5.030765s
  changed_attributes     51.530k (±14.2%) i/s -    252.764k in   5.041106s
        title_change     44.667k (± 9.0%) i/s -    222.950k in   5.040646s
           title_was     44.635k (±16.6%) i/s -    216.384k in   5.051098s
```

After:

```
Warming up --------------------------------------
            changed?    24.130k i/100ms
             changed    13.503k i/100ms
             changes     6.511k i/100ms
  changed_attributes     9.226k i/100ms
        title_change    48.221k i/100ms
           title_was    96.060k i/100ms
Calculating -------------------------------------
            changed?    245.478k (±16.1%) i/s -      1.182M in   5.015837s
             changed    157.641k (± 4.9%) i/s -    796.677k in   5.066734s
             changes     70.633k (± 5.7%) i/s -    358.105k in   5.086553s
  changed_attributes     95.155k (±13.6%) i/s -    470.526k in   5.082841s
        title_change    566.481k (± 3.5%) i/s -      2.845M in   5.028852s
           title_was      1.487M (± 3.9%) i/s -      7.493M in   5.046774s
```
2019-04-11 16:30:40 +09:00
Ryuta Kamizono eb3740dcb0
Merge pull request #32498 from eugeneius/mutation_tracker_merge_changes
Prevent changes_to_save from mutating attributes
2018-04-10 13:18:34 +09:00
Eugene Kenny 80a09caedc Prevent changes_to_save from mutating attributes
When an array of hashes is added to a `HashWithIndifferentAccess`, the
hashes are replaced with HWIAs by mutating the array in place.

If an attribute's value is an array of hashes, `changes_to_save` will
convert it to an array of HWIAs as a side-effect of adding it to the
changes hash.

Using `merge!` instead of `[]=` fixes the problem, as `merge!` copies
any array values in the provided hash instead of mutating them.
2018-04-08 23:06:48 +01:00
Eugene Kenny b9e1c0c4d7 Avoid generating full changes hash on every save
`changed_attribute_names_to_save` is called in `keys_for_partial_write`,
which is called on every save when partial writes are enabled.

We can avoid generating the full changes hash by asking the mutation
tracker for just the names of the changed attributes. At minimum this
saves one array allocation per attribute, but will also avoid calling
`Attribute#original_value` which is expensive for serialized attributes.
2018-04-08 22:56:31 +01:00
Sean Griffin 5fcbdcfb57 Revert "PERF: Recover `changes_applied` performance (#31698)"
This reverts commit a19e91f0fa.
2018-03-06 15:11:22 -07:00
Jeremy Daer 1e526788e6 Rails 6 requires Ruby 2.3+ 2018-02-17 10:03:37 -08:00
Ryuta Kamizono a19e91f0fa
PERF: Recover `changes_applied` performance (#31698)
#30985 caused `object.save` performance regression since calling
`changes` in `changes_applied` is very slow.
We don't need to call the expensive method in `changes_applied` as long
as `@attributes` is tracked by mutation tracker.

https://gist.github.com/kamipo/1a9f4f3891803b914fc72ede98268aa2

Before:

```
Warming up --------------------------------------
create_string_columns
                        73.000  i/100ms
Calculating -------------------------------------
create_string_columns
                        722.256  (± 5.8%) i/s -      3.650k in   5.073031s
```

After:

```
Warming up --------------------------------------
create_string_columns
                        96.000  i/100ms
Calculating -------------------------------------
create_string_columns
                        950.224  (± 7.7%) i/s -      4.800k in   5.084837s
```
2018-01-22 10:46:36 +09:00
Ryuta Kamizono fc7a6c7381 Add missing require "active_support/core_ext/hash/indifferent_access"
https://travis-ci.org/rails/rails/jobs/300163454#L2236
2017-11-10 23:48:53 +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