-
Jean Boussier authored
Because `#present?` always resolve to `Object#present?`, it's an extremely polymorphic method, and inline cache hits are low. In addition, it requires an extra call to `self.blank?` which is an overhead. By specializing `present?` on common types, we avoid both of these slow-downs: ``` ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22] Warming up -------------------------------------- present? 198.028k i/100ms opt_present? 565.521k i/100ms Calculating ------------------------------------- present? 2.087M (± 8.8%) i/s - 10.297M in 5.028398s opt_present? 5.584M (± 8.6%) i/s - 27.711M in 5.023852s Comparison: present?: 2086621.6 i/s opt_present?: 5584373.5 i/s - 2.68x faster ``` ``` ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22] Warming up -------------------------------------- present? 819.792k i/100ms opt_present? 1.047M i/100ms Calculating ------------------------------------- present? 12.192M (± 8.8%) i/s - 60.665M in 5.050622s opt_present? 16.540M (± 8.2%) i/s - 82.676M in 5.059029s Comparison: present?: 12192047.5 i/s opt_present?: 16539689.6 i/s - 1.36x faster ``` ```ruby require 'bundler/inline' gemfile do source 'https://rubygems.org' gem 'benchmark-ips' gem 'activesupport' end require 'active_support/all' class Object def opt_present? respond_to?(:empty?) ? !empty? : !!self end end class NilClass def opt_present? false end end class FalseClass def opt_present? false end end class TrueClass def opt_present? true end end class Array def opt_present? !empty? end end class Hash def opt_present? !empty? end end class Symbol def opt_present? !empty? end end class String def opt_present? !blank? end end class Numeric # :nodoc: def opt_present? true end end class Time # :nodoc: def opt_present? true end end array = [] hash = {} time = Time.now puts RUBY_DESCRIPTION Benchmark.ips do |x| x.report("present?") do true.present? false.present? 1.present? 1.0.present? array.present? hash.present? :foo.present? time.present? end x.report("opt_present?") do true.opt_present? false.opt_present? 1.opt_present? 1.0.opt_present? array.opt_present? hash.opt_present? :foo.opt_present? time.opt_present? end x.compare!(order: :baseline) end ```
Jean Boussier authoredBecause `#present?` always resolve to `Object#present?`, it's an extremely polymorphic method, and inline cache hits are low. In addition, it requires an extra call to `self.blank?` which is an overhead. By specializing `present?` on common types, we avoid both of these slow-downs: ``` ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22] Warming up -------------------------------------- present? 198.028k i/100ms opt_present? 565.521k i/100ms Calculating ------------------------------------- present? 2.087M (± 8.8%) i/s - 10.297M in 5.028398s opt_present? 5.584M (± 8.6%) i/s - 27.711M in 5.023852s Comparison: present?: 2086621.6 i/s opt_present?: 5584373.5 i/s - 2.68x faster ``` ``` ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22] Warming up -------------------------------------- present? 819.792k i/100ms opt_present? 1.047M i/100ms Calculating ------------------------------------- present? 12.192M (± 8.8%) i/s - 60.665M in 5.050622s opt_present? 16.540M (± 8.2%) i/s - 82.676M in 5.059029s Comparison: present?: 12192047.5 i/s opt_present?: 16539689.6 i/s - 1.36x faster ``` ```ruby require 'bundler/inline' gemfile do source 'https://rubygems.org' gem 'benchmark-ips' gem 'activesupport' end require 'active_support/all' class Object def opt_present? respond_to?(:empty?) ? !empty? : !!self end end class NilClass def opt_present? false end end class FalseClass def opt_present? false end end class TrueClass def opt_present? true end end class Array def opt_present? !empty? end end class Hash def opt_present? !empty? end end class Symbol def opt_present? !empty? end end class String def opt_present? !blank? end end class Numeric # :nodoc: def opt_present? true end end class Time # :nodoc: def opt_present? true end end array = [] hash = {} time = Time.now puts RUBY_DESCRIPTION Benchmark.ips do |x| x.report("present?") do true.present? false.present? 1.present? 1.0.present? array.present? hash.present? :foo.present? time.present? end x.report("opt_present?") do true.opt_present? false.opt_present? 1.opt_present? 1.0.opt_present? array.opt_present? hash.opt_present? :foo.opt_present? time.opt_present? end x.compare!(order: :baseline) end ```
Loading