Key the attributes hash with symbols

This is a performance/GC optimisation.

In theory, this could be optimised by the implementation (last time I
checked, this would have no effect on JRuby). But in practise, this make
attribute access faster.
This commit is contained in:
Jon Leighton 2012-08-31 16:55:08 +01:00
parent e96558f813
commit 86c3dfbd47
4 changed files with 14 additions and 9 deletions

View File

@ -45,7 +45,7 @@ module ActiveRecord
def define_method_attribute(attr_name) def define_method_attribute(attr_name)
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__ def __temp__
read_attribute('#{attr_name}') { |n| missing_attribute(n, caller) } read_attribute(:'#{attr_name}') { |n| missing_attribute(n, caller) }
end end
alias_method '#{attr_name}', :__temp__ alias_method '#{attr_name}', :__temp__
undef_method :__temp__ undef_method :__temp__
@ -68,11 +68,16 @@ module ActiveRecord
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example, # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name) def read_attribute(attr_name)
return unless attr_name
name_sym = attr_name.to_sym
# If it's cached, just return it # If it's cached, just return it
@attributes_cache.fetch(attr_name.to_s) { |name| @attributes_cache.fetch(name_sym) {
name = attr_name.to_s
column = @columns_hash.fetch(name) { column = @columns_hash.fetch(name) {
return @attributes.fetch(name) { return @attributes.fetch(name) {
if name == 'id' && self.class.primary_key != name if name_sym == :id && self.class.primary_key != name
read_attribute(self.class.primary_key) read_attribute(self.class.primary_key)
end end
} }
@ -83,7 +88,7 @@ module ActiveRecord
} }
if self.class.cache_attribute?(name) if self.class.cache_attribute?(name)
@attributes_cache[name] = column.type_cast(value) @attributes_cache[name_sym] = column.type_cast(value)
else else
column.type_cast value column.type_cast value
end end

View File

@ -65,7 +65,7 @@ module ActiveRecord
if (rounded_value != rounded_time) || (!rounded_value && original_time) if (rounded_value != rounded_time) || (!rounded_value && original_time)
write_attribute("#{attr_name}", original_time) write_attribute("#{attr_name}", original_time)
#{attr_name}_will_change! #{attr_name}_will_change!
@attributes_cache["#{attr_name}"] = zoned_time @attributes_cache[:"#{attr_name}"] = zoned_time
end end
end end
EOV EOV

View File

@ -25,13 +25,13 @@ module ActiveRecord
def write_attribute(attr_name, value) def write_attribute(attr_name, value)
attr_name = attr_name.to_s attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
@attributes_cache.delete(attr_name) @attributes_cache.delete(attr_name.to_sym)
column = column_for_attribute(attr_name) column = column_for_attribute(attr_name)
# If we're dealing with a binary column, write the data to the cache # If we're dealing with a binary column, write the data to the cache
# so we don't attempt to typecast multiple times. # so we don't attempt to typecast multiple times.
if column && column.binary? if column && column.binary?
@attributes_cache[attr_name] = value @attributes_cache[attr_name.to_sym] = value
end end
if column || @attributes.has_key?(attr_name) if column || @attributes.has_key?(attr_name)

View File

@ -542,10 +542,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase
val = t.send attr_name unless attr_name == "type" val = t.send attr_name unless attr_name == "type"
if attribute_gets_cached if attribute_gets_cached
assert cached_columns.include?(attr_name) assert cached_columns.include?(attr_name)
assert_equal val, cache[attr_name] assert_equal val, cache[attr_name.to_sym]
else else
assert uncached_columns.include?(attr_name) assert uncached_columns.include?(attr_name)
assert !cache.include?(attr_name) assert !cache.include?(attr_name.to_sym)
end end
end end
end end