Skip to content

Commit 0c25a0b

Browse files
committed
Micro-optimize ActiveRecord::Core#hash
Avoids calling _read_attribute("id") more than necessary ```ruby require "active_record" require "benchmark/ips" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.connection.create_table :users do |t| t.text :name end class User < ActiveRecord::Base # This is the current implementation from ActiveRecord::Core def hash if id self.class.hash ^ id.hash else super end end end class UserFastHash < ActiveRecord::Base self.table_name = "users" def hash if i = id self.class.hash ^ i.hash else super end end end 1_000.times { |i| User.create(name: "test #{i}") } slow_users = User.take(1000) fast_users = UserFastHash.take(1000) Benchmark.ips do |x| x.report("slowhash") { hash = {} slow_users.each { |u| hash[u] = 1 } } x.report("fasthash") { hash = {} fast_users.each { |u| hash[u] = 1 } } x.compare! end ``` ``` Warming up -------------------------------------- slowhash 129.000 i/100ms fasthash 177.000 i/100ms Calculating ------------------------------------- slowhash 1.307k (± 0.7%) i/s - 6.579k in 5.033141s fasthash 1.764k (± 2.4%) i/s - 8.850k in 5.021749s Comparison: fasthash: 1763.5 i/s slowhash: 1307.2 i/s - 1.35x (± 0.00) slower ```
1 parent 7373b58 commit 0c25a0b

File tree

1 file changed

+2
-0
lines changed
  • activerecord/lib/active_record

1 file changed

+2
-0
lines changed

activerecord/lib/active_record/core.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,8 @@ def ==(comparison_object)
601601
# Delegates to id in order to allow two records of the same type and id to work with something like:
602602
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
603603
def hash
604+
id = self.id
605+
604606
if id
605607
self.class.hash ^ id.hash
606608
else

0 commit comments

Comments
 (0)