提交 8fee9238 编写于 作者: S Sean Griffin

Improve performance of AR object instantiation

We introduced a performance hit by adding an additional iteration
through a model's attributes on creation. We don't actually need the
values from `Result` to be a hash, we can separate the columns and
values and zip them up ourself during the iteration that we have to do.
上级 00ae750b
......@@ -8,18 +8,33 @@ def initialize(types)
end
def build_from_database(values = {}, additional_types = {})
attributes = build_attributes_from_values(values, additional_types)
build_from_database_pairs(values.keys, values.values, additional_types)
end
def build_from_database_pairs(columns = [], values = [], additional_types = {})
attributes = build_attributes_from_values(columns, values, additional_types)
add_uninitialized_attributes(attributes)
AttributeSet.new(attributes)
end
private
def build_attributes_from_values(values, additional_types)
values.each_with_object({}) do |(name, value), hash|
def build_attributes_from_values(columns, values, additional_types)
# We are performing manual iteration here as this method is a performance
# hotspot
hash = {}
index = 0
length = columns.length
while index < length
name = columns[index]
value = values[index]
type = additional_types.fetch(name, types[name])
hash[name] = Attribute.from_database(name, value, type)
index += 1
end
hash
end
def add_uninitialized_attributes(attributes)
......
......@@ -165,15 +165,19 @@ def compute_type(type_name)
# record instance. For single-table inheritance, we check the record
# for a +type+ column and return the corresponding class.
def discriminate_class_for_record(record)
if using_single_table_inheritance?(record)
find_sti_class(record[inheritance_column])
discriminate_class_for_value(record[inheritance_column])
end
def discriminate_class_for_value(value)
if using_single_table_inheritance?(value)
find_sti_class(value)
else
super
end
end
def using_single_table_inheritance?(record)
record[inheritance_column].present? && columns_hash.include?(inheritance_column)
def using_single_table_inheritance?(value)
value.present? && columns_hash.include?(inheritance_column)
end
def find_sti_class(type_name)
......
......@@ -65,19 +65,41 @@ def create!(attributes = nil, &block)
# how this "single-table" inheritance mapping is implemented.
def instantiate(attributes, column_types = {})
klass = discriminate_class_for_record(attributes)
attributes = klass.attributes_builder.build_from_database(attributes, column_types)
klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
klass.instantiate_pairs(attributes.keys, attributes.values, column_types)
end
def instantiate_pairs(columns, values, column_types = {}) # :nodoc:
attributes = attributes_builder.build_from_database_pairs(columns, values, column_types)
allocate.init_with('attributes' => attributes, 'new_record' => false)
end
def instantiate_result_set(result_set, column_types = {}) # :nodoc:
inheritance_column_index = inheritance_column && result_set.columns.find_index(inheritance_column)
result_set.each_pair.map do |columns, values|
inheritance_value = inheritance_column_index && values[inheritance_column_index]
klass = discriminate_class_for_value(inheritance_value)
klass.instantiate_pairs(columns, values, column_types)
end
end
private
# Called by +instantiate+ to decide which class to use for a new
# record instance.
#
# See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
# See +ActiveRecord::Inheritance#discriminate_class_for_value+ for
# the single-table inheritance discriminator.
def discriminate_class_for_value(*)
self
end
def discriminate_class_for_record(record)
self
end
def inheritance_column
nil
end
end
# Returns true if this object hasn't been saved yet -- that is, a record
......
......@@ -47,7 +47,7 @@ def find_by_sql(sql, binds = [])
}
message_bus.instrument('instantiation.active_record', payload) do
result_set.map { |record| instantiate(record, column_types) }
instantiate_result_set(result_set, column_types)
end
end
......
......@@ -54,6 +54,15 @@ def each
end
end
def each_pair
return to_enum(__method__) unless block_given?
columns = @columns.map { |c| c.dup.freeze }
@rows.each do |row|
yield columns, row
end
end
def to_hash
hash_rows
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册