mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
7d58fa87ef
This commit speeds up allocating homogeneous lists of AR objects. We can know if the result set contains an STI column before initializing every AR object, so this change pulls the "does this result set contain an STI column?" test up, then uses a specialized instantiation function. This way we only have to check for an STI column once rather than N times. This change also introduces a new initialization function that is meant for use when allocating AR objects that come from the database. Doing this allows us to eliminate one hash allocation per AR instance. Here is a benchmark: ```ruby require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:" ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name t.timestamps null: false end end class User < ActiveRecord::Base; end 2000.times do User.create!(name: "Gorby") end Benchmark.ips do |x| x.report("find") do User.limit(2000).to_a end end ``` Results: Before: ``` [aaron@TC activerecord (master)]$ be ruby -I lib:~/git/allocation_tracer/lib speed.rb Warming up -------------------------------------- find 5.000 i/100ms Calculating ------------------------------------- find 56.192 (± 3.6%) i/s - 285.000 in 5.080940s ``` After: ``` [aaron@TC activerecord (homogeneous-allocation)]$ be ruby -I lib:~/git/allocation_tracer/lib speed.rb Warming up -------------------------------------- find 7.000 i/100ms Calculating ------------------------------------- find 72.204 (± 2.8%) i/s - 364.000 in 5.044592s ```
95 lines
2.8 KiB
Ruby
95 lines
2.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "cases/helper"
|
|
|
|
module ActiveRecord
|
|
class ResultTest < ActiveRecord::TestCase
|
|
def result
|
|
Result.new(["col_1", "col_2"], [
|
|
["row 1 col 1", "row 1 col 2"],
|
|
["row 2 col 1", "row 2 col 2"],
|
|
["row 3 col 1", "row 3 col 2"],
|
|
])
|
|
end
|
|
|
|
test "includes_column?" do
|
|
assert result.includes_column?("col_1")
|
|
assert_not result.includes_column?("foo")
|
|
end
|
|
|
|
test "length" do
|
|
assert_equal 3, result.length
|
|
end
|
|
|
|
test "to_hash returns row_hashes" do
|
|
assert_equal [
|
|
{ "col_1" => "row 1 col 1", "col_2" => "row 1 col 2" },
|
|
{ "col_1" => "row 2 col 1", "col_2" => "row 2 col 2" },
|
|
{ "col_1" => "row 3 col 1", "col_2" => "row 3 col 2" },
|
|
], result.to_hash
|
|
end
|
|
|
|
test "first returns first row as a hash" do
|
|
assert_equal(
|
|
{ "col_1" => "row 1 col 1", "col_2" => "row 1 col 2" }, result.first)
|
|
end
|
|
|
|
test "last returns last row as a hash" do
|
|
assert_equal(
|
|
{ "col_1" => "row 3 col 1", "col_2" => "row 3 col 2" }, result.last)
|
|
end
|
|
|
|
test "each with block returns row hashes" do
|
|
result.each do |row|
|
|
assert_equal ["col_1", "col_2"], row.keys
|
|
end
|
|
end
|
|
|
|
test "each without block returns an enumerator" do
|
|
result.each.with_index do |row, index|
|
|
assert_equal ["col_1", "col_2"], row.keys
|
|
assert_kind_of Integer, index
|
|
end
|
|
end
|
|
|
|
test "each without block returns a sized enumerator" do
|
|
assert_equal 3, result.each.size
|
|
end
|
|
|
|
test "cast_values returns rows after type casting" do
|
|
values = [["1.1", "2.2"], ["3.3", "4.4"]]
|
|
columns = ["col1", "col2"]
|
|
types = { "col1" => Type::Integer.new, "col2" => Type::Float.new }
|
|
result = Result.new(columns, values, types)
|
|
|
|
assert_equal [[1, 2.2], [3, 4.4]], result.cast_values
|
|
end
|
|
|
|
test "cast_values uses identity type for unknown types" do
|
|
values = [["1.1", "2.2"], ["3.3", "4.4"]]
|
|
columns = ["col1", "col2"]
|
|
types = { "col1" => Type::Integer.new }
|
|
result = Result.new(columns, values, types)
|
|
|
|
assert_equal [[1, "2.2"], [3, "4.4"]], result.cast_values
|
|
end
|
|
|
|
test "cast_values returns single dimensional array if single column" do
|
|
values = [["1.1"], ["3.3"]]
|
|
columns = ["col1"]
|
|
types = { "col1" => Type::Integer.new }
|
|
result = Result.new(columns, values, types)
|
|
|
|
assert_equal [1, 3], result.cast_values
|
|
end
|
|
|
|
test "cast_values can receive types to use instead" do
|
|
values = [["1.1", "2.2"], ["3.3", "4.4"]]
|
|
columns = ["col1", "col2"]
|
|
types = { "col1" => Type::Integer.new, "col2" => Type::Float.new }
|
|
result = Result.new(columns, values, types)
|
|
|
|
assert_equal [[1.1, 2.2], [3.3, 4.4]], result.cast_values("col1" => Type::Float.new)
|
|
end
|
|
end
|
|
end
|