Avoid generating full changes hash on every save #32497
Merged
+9
−1
Conversation
`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.
Benchmarks:Simple case: model with no attributesbegin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rails", path: "."
gem "sqlite3"
gem "benchmark-ips"
end
require "active_record"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
end
end
class Post < ActiveRecord::Base
end
post = Post.new
Benchmark.ips do |x|
x.report("mutations_from_database.changed_attribute_names") { post.changed_attribute_names_to_save }
x.report("changes_to_save.keys") { post.changes_to_save.keys }
end
Complex case: model with a large serialized attributebegin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rails", path: "."
gem "sqlite3"
gem "benchmark-ips"
end
require "active_record"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.string :content
end
end
class Post < ActiveRecord::Base
serialize :content
end
post = Post.new(content: [{ foo: "bar" }] * 100)
Benchmark.ips do |x|
x.report("mutations_from_database.changed_attribute_names") { post.changed_attribute_names_to_save }
x.report("changes_to_save.keys") { post.changes_to_save.keys }
end
|
This improvement was accidentally lost in the revert commit 5fcbdcf. |
rafaelfranca
added a commit
that referenced
this pull request
Apr 9, 2018
…ribute_names Avoid generating full changes hash on every save
kamipo
added a commit
that referenced
this pull request
Apr 10, 2018
…ribute_names Avoid generating full changes hash on every save
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
changed_attribute_names_to_save
is called inkeys_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.r? @sgrif