mirror of https://github.com/rails/rails
PERF: 15% faster attribute access
Delegating to just one line method is to not be worth it. Avoiding the delegation makes `read_attribute` about 15% faster. ```ruby ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name end end class User < ActiveRecord::Base def fast_read_attribute(attr_name, &block) name = attr_name.to_s name = self.class.attribute_aliases[name] || name name = @primary_key if name == "id" && @primary_key @attributes.fetch_value(name, &block) end end user = User.create!(name: "user name") Benchmark.ips do |x| x.report("read_attribute('id')") { user.read_attribute('id') } x.report("read_attribute('name')") { user.read_attribute('name') } x.report("fast_read_attribute('id')") { user.fast_read_attribute('id') } x.report("fast_read_attribute('name')") { user.fast_read_attribute('name') } end ``` ``` Warming up -------------------------------------- read_attribute('id') 165.744k i/100ms read_attribute('name') 162.229k i/100ms fast_read_attribute('id') 192.543k i/100ms fast_read_attribute('name') 191.209k i/100ms Calculating ------------------------------------- read_attribute('id') 1.648M (± 1.7%) i/s - 8.287M in 5.030170s read_attribute('name') 1.636M (± 3.9%) i/s - 8.274M in 5.065356s fast_read_attribute('id') 1.918M (± 1.8%) i/s - 9.627M in 5.021271s fast_read_attribute('name') 1.928M (± 0.9%) i/s - 9.752M in 5.058820s ```
This commit is contained in:
parent
b0231a7aa0
commit
27a1ca2bfe
|
@ -48,10 +48,12 @@ module ActiveModel
|
|||
def write_from_user(name, value)
|
||||
raise FrozenError, "can't modify frozen attributes" if frozen?
|
||||
attributes[name] = self[name].with_value_from_user(value)
|
||||
value
|
||||
end
|
||||
|
||||
def write_cast_value(name, value)
|
||||
attributes[name] = self[name].with_cast_value(value)
|
||||
value
|
||||
end
|
||||
|
||||
def freeze
|
||||
|
|
|
@ -48,7 +48,7 @@ module ActiveModel
|
|||
) do |temp_method_name, attr_name_expr|
|
||||
owner <<
|
||||
"def #{temp_method_name}(value)" <<
|
||||
" write_attribute(#{attr_name_expr}, value)" <<
|
||||
" _write_attribute(#{attr_name_expr}, value)" <<
|
||||
"end"
|
||||
end
|
||||
end
|
||||
|
@ -124,12 +124,11 @@ module ActiveModel
|
|||
name = attr_name.to_s
|
||||
name = self.class.attribute_aliases[name] || name
|
||||
|
||||
_write_attribute(name, value)
|
||||
@attributes.write_from_user(name, value)
|
||||
end
|
||||
|
||||
def _write_attribute(attr_name, value)
|
||||
@attributes.write_from_user(attr_name, value)
|
||||
value
|
||||
end
|
||||
alias :attribute= :_write_attribute
|
||||
|
||||
|
@ -137,12 +136,11 @@ module ActiveModel
|
|||
name = attr_name.to_s
|
||||
name = self.class.attribute_aliases[name] || name
|
||||
|
||||
_read_attribute(name)
|
||||
@attributes.fetch_value(name)
|
||||
end
|
||||
|
||||
def _read_attribute(attr_name)
|
||||
def attribute(attr_name)
|
||||
@attributes.fetch_value(attr_name)
|
||||
end
|
||||
alias :attribute :_read_attribute
|
||||
end
|
||||
end
|
||||
|
|
|
@ -182,7 +182,7 @@ module ActiveRecord
|
|||
def has_attribute?(attr_name)
|
||||
attr_name = attr_name.to_s
|
||||
attr_name = attribute_aliases[attr_name] || attr_name
|
||||
_has_attribute?(attr_name)
|
||||
attribute_types.key?(attr_name)
|
||||
end
|
||||
|
||||
def _has_attribute?(attr_name) # :nodoc:
|
||||
|
@ -256,7 +256,7 @@ module ActiveRecord
|
|||
def has_attribute?(attr_name)
|
||||
attr_name = attr_name.to_s
|
||||
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
||||
_has_attribute?(attr_name)
|
||||
@attributes.key?(attr_name)
|
||||
end
|
||||
|
||||
def _has_attribute?(attr_name) # :nodoc:
|
||||
|
|
|
@ -27,7 +27,7 @@ module ActiveRecord
|
|||
name = self.class.attribute_aliases[name] || name
|
||||
|
||||
name = @primary_key if name == "id" && @primary_key
|
||||
_read_attribute(name, &block)
|
||||
@attributes.fetch_value(name, &block)
|
||||
end
|
||||
|
||||
# This method exists to avoid the expensive primary_key check internally, without
|
||||
|
|
|
@ -31,14 +31,13 @@ module ActiveRecord
|
|||
name = self.class.attribute_aliases[name] || name
|
||||
|
||||
name = @primary_key if name == "id" && @primary_key
|
||||
_write_attribute(name, value)
|
||||
@attributes.write_from_user(name, value)
|
||||
end
|
||||
|
||||
# This method exists to avoid the expensive primary_key check internally, without
|
||||
# breaking compatibility with the write_attribute API
|
||||
def _write_attribute(attr_name, value) # :nodoc:
|
||||
@attributes.write_from_user(attr_name, value)
|
||||
value
|
||||
end
|
||||
|
||||
alias :attribute= :_write_attribute
|
||||
|
@ -47,7 +46,6 @@ module ActiveRecord
|
|||
private
|
||||
def write_attribute_without_type_cast(attr_name, value)
|
||||
@attributes.write_cast_value(attr_name, value)
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue