Created
August 27, 2024 11:25
-
-
Save casperisfine/539e310eadd0cb650fc50ca0d9782569 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'bundler/inline' | |
gemfile(true) do | |
source 'https://rubygems.org' | |
gem 'activesupport', github: 'rails/rails' | |
gem 'benchmark-ips' | |
end | |
require 'benchmark/ips' | |
require "active_support/core_ext/class/attribute" | |
module ActiveSupport | |
module ClassAttribute # :nodoc: | |
class << self | |
def redefine(owner, name, value) | |
if owner.singleton_class? | |
owner.redefine_method(name) { value } | |
owner.silence_redefinition_of_method(name) | |
end | |
owner.redefine_singleton_method(name) { value } | |
owner.redefine_singleton_method("#{name}=") do |new_value| | |
if owner.equal?(self) | |
value = new_value | |
else | |
::ActiveSupport::ClassAttribute.redefine(self, name, new_value) | |
end | |
end | |
end | |
end | |
end | |
end | |
class Class | |
def faster_class_attribute(*attrs, instance_accessor: true, | |
instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil) | |
class_methods, methods = [], [] | |
attrs.each do |name| | |
unless name.is_a?(Symbol) || name.is_a?(String) | |
raise TypeError, "#{name.inspect} is not a symbol nor a string" | |
end | |
name = name.to_sym | |
::ActiveSupport::ClassAttribute.redefine(self, name, default) | |
unless singleton_class? | |
methods << <<~RUBY if instance_reader | |
silence_redefinition_of_method def #{name} | |
defined?(@#{name}) ? @#{name} : self.class.#{name} | |
end | |
RUBY | |
end | |
methods << <<~RUBY if instance_writer | |
silence_redefinition_of_method(:#{name}=) | |
attr_writer :#{name} | |
RUBY | |
if instance_predicate | |
class_methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end" | |
if instance_reader | |
methods << "silence_redefinition_of_method def #{name}?; !!self.#{name}; end" | |
end | |
end | |
end | |
location = caller_locations(1, 1).first | |
class_eval(["class << self", *class_methods, "end", *methods].join(";").tr("\n", ";"), location.path, location.lineno) | |
end | |
end | |
class Foo | |
class_attribute :old, :old_too | |
faster_class_attribute :fast, :fast_too | |
end | |
class Bar < Foo | |
end | |
class Baz < Bar | |
end | |
class Other | |
end | |
FOO_INSTANCE = Foo.new | |
puts "==== definition ====" | |
Benchmark.ips do |x| | |
x.report("old") { Other.class_attribute :attr1, :attr2, :attr3, :attr4, :attr5, :attr6, :attr7, :attr8, :attr9, :attr10 } | |
x.report("new") { Other.faster_class_attribute :attr1, :attr2, :attr3, :attr4, :attr5, :attr6, :attr7, :attr8, :attr9, :attr10 } | |
x.compare!(order: :baseline) | |
end | |
puts "==== class reader ====" | |
Benchmark.ips do |x| | |
x.report("old") { Foo.old } | |
x.report("new") { Foo.fast } | |
x.compare!(order: :baseline) | |
end | |
puts "==== class inherited reader ====" | |
Benchmark.ips do |x| | |
x.report("old") { Baz.old } | |
x.report("new") { Baz.fast } | |
x.compare!(order: :baseline) | |
end | |
puts "==== class writer ====" | |
Benchmark.ips do |x| | |
x.report("old") { Foo.old = 42 } | |
x.report("new") { Foo.fast = 42 } | |
x.compare!(order: :baseline) | |
end | |
puts "==== instance reader ====" | |
Benchmark.ips do |x| | |
x.report("old") { FOO_INSTANCE.old } | |
x.report("new") { FOO_INSTANCE.fast } | |
x.compare!(order: :baseline) | |
end | |
puts "==== instance writer ====" | |
Benchmark.ips do |x| | |
x.report("old") { FOO_INSTANCE.old = 42} | |
x.report("new") { FOO_INSTANCE.fast = 42 } | |
x.compare!(order: :baseline) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ruby /tmp/class_attribute.rb | |
Fetching gem metadata from https://rubygems.org/.......... | |
Resolving dependencies... | |
==== definition ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 359.000 i/100ms | |
new 648.000 i/100ms | |
Calculating ------------------------------------- | |
old 3.782k (± 4.4%) i/s - 19.027k in 5.040409s | |
new 6.684k (± 3.7%) i/s - 33.696k in 5.047975s | |
Comparison: | |
old: 3782.3 i/s | |
new: 6684.2 i/s - 1.77x faster | |
==== class reader ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 3.472M i/100ms | |
new 3.477M i/100ms | |
Calculating ------------------------------------- | |
old 34.627M (± 1.5%) i/s - 173.583M in 5.014081s | |
new 33.503M (± 1.3%) i/s - 170.353M in 5.085518s | |
Comparison: | |
old: 34627278.8 i/s | |
new: 33503236.6 i/s - 1.03x slower | |
==== class inherited reader ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 3.543M i/100ms | |
new 3.539M i/100ms | |
Calculating ------------------------------------- | |
old 34.971M (± 2.5%) i/s - 177.131M in 5.068421s | |
new 35.316M (± 0.7%) i/s - 176.926M in 5.010014s | |
Comparison: | |
old: 34971144.7 i/s | |
new: 35316427.3 i/s - same-ish: difference falls within error | |
==== class writer ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 184.659k i/100ms | |
new 1.769M i/100ms | |
Calculating ------------------------------------- | |
old 1.834M (± 1.5%) i/s - 9.233M in 5.035024s | |
new 17.722M (± 2.9%) i/s - 90.220M in 5.095668s | |
Comparison: | |
old: 1834149.6 i/s | |
new: 17722346.6 i/s - 9.66x faster | |
==== instance reader ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 1.807M i/100ms | |
new 1.804M i/100ms | |
Calculating ------------------------------------- | |
old 18.065M (± 2.7%) i/s - 90.366M in 5.006426s | |
new 18.149M (± 1.0%) i/s - 91.989M in 5.068965s | |
Comparison: | |
old: 18065296.8 i/s | |
new: 18149220.0 i/s - same-ish: difference falls within error | |
==== instance writer ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 3.888M i/100ms | |
new 3.829M i/100ms | |
Calculating ------------------------------------- | |
old 38.740M (± 2.8%) i/s - 194.413M in 5.022806s | |
new 38.528M (± 2.4%) i/s - 195.269M in 5.071387s | |
Comparison: | |
old: 38740467.7 i/s | |
new: 38528036.9 i/s - same-ish: difference falls within error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ruby --yjit /tmp/class_attribute.rb | |
Fetching gem metadata from https://rubygems.org/.......... | |
Resolving dependencies... | |
==== definition ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 363.000 i/100ms | |
new 671.000 i/100ms | |
Calculating ------------------------------------- | |
old 3.745k (± 3.6%) i/s - 18.876k in 5.047932s | |
new 6.612k (± 3.2%) i/s - 33.550k in 5.079578s | |
Comparison: | |
old: 3744.5 i/s | |
new: 6611.9 i/s - 1.77x faster | |
==== class reader ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 4.406M i/100ms | |
new 4.269M i/100ms | |
Calculating ------------------------------------- | |
old 43.817M (± 4.5%) i/s - 220.324M in 5.038915s | |
new 44.779M (± 2.8%) i/s - 226.263M in 5.057146s | |
Comparison: | |
old: 43816694.4 i/s | |
new: 44778662.3 i/s - same-ish: difference falls within error | |
==== class inherited reader ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 4.609M i/100ms | |
new 4.420M i/100ms | |
Calculating ------------------------------------- | |
old 44.458M (± 4.5%) i/s - 225.817M in 5.090329s | |
new 44.931M (± 3.2%) i/s - 225.431M in 5.022544s | |
Comparison: | |
old: 44457947.2 i/s | |
new: 44930756.7 i/s - same-ish: difference falls within error | |
==== class writer ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 251.197k i/100ms | |
new 4.098M i/100ms | |
Calculating ------------------------------------- | |
old 2.561M (± 2.1%) i/s - 12.811M in 5.004196s | |
new 43.004M (± 3.3%) i/s - 217.210M in 5.056701s | |
Comparison: | |
old: 2561173.8 i/s | |
new: 43004422.5 i/s - 16.79x faster | |
==== instance reader ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 4.007M i/100ms | |
new 3.908M i/100ms | |
Calculating ------------------------------------- | |
old 38.408M (± 4.3%) i/s - 192.318M in 5.017940s | |
new 38.316M (± 3.3%) i/s - 195.391M in 5.105697s | |
Comparison: | |
old: 38408490.1 i/s | |
new: 38316383.2 i/s - same-ish: difference falls within error | |
==== instance writer ==== | |
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23] | |
Warming up -------------------------------------- | |
old 3.914M i/100ms | |
new 4.035M i/100ms | |
Calculating ------------------------------------- | |
old 40.173M (± 1.9%) i/s - 203.548M in 5.068536s | |
new 40.273M (± 3.1%) i/s - 201.771M in 5.015094s | |
Comparison: | |
old: 40172937.9 i/s | |
new: 40273498.6 i/s - same-ish: difference falls within error |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment