From 31c20e248a518421226ee774f3a3cdab61c1e386 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 30 Mar 2021 15:41:20 +0200 Subject: [PATCH] Optimize ActiveSupport::NumericWithFormat#to_s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `case format when nil` is very efficient because it end up calling `NilClass === nil` which pretty much translates to `nil.is_a?(NilClass)`. On the other hand `format.nil?` benefit from a dedicated op code, so it's quite faster. In this case `Integer#to_s` is much more often called without any arguments, so it's worth optimizing for the most common case. ```ruby class Integer alias_method :faster_to_s, :to_s end require 'active_support/all' require 'benchmark/ips' module FasterNumericWithFormat def faster_to_s(format = nil, options = nil) if format.nil? return super() end case format when Integer, String super(format) when :phone ActiveSupport::NumberHelper.number_to_phone(self, options || {}) when :currency ActiveSupport::NumberHelper.number_to_currency(self, options || {}) when :percentage ActiveSupport::NumberHelper.number_to_percentage(self, options || {}) when :delimited ActiveSupport::NumberHelper.number_to_delimited(self, options || {}) when :rounded ActiveSupport::NumberHelper.number_to_rounded(self, options || {}) when :human ActiveSupport::NumberHelper.number_to_human(self, options || {}) when :human_size ActiveSupport::NumberHelper.number_to_human_size(self, options || {}) when Symbol super() else super(format) end end end Integer.prepend(FasterNumericWithFormat) Benchmark.ips do |x| x.report('orig no-arg') { 42.to_s } x.report('fast no-arg') { 42.faster_to_s } x.compare! end Benchmark.ips do |x| x.report('orig :human') { 42.to_s(:human) } x.report('fast :human') { 42.faster_to_s(:human) } x.compare! end ``` Ruby 2.7.2 ``` Warming up -------------------------------------- orig no-arg 567.569k i/100ms fast no-arg 692.636k i/100ms Calculating ------------------------------------- orig no-arg 5.709M (± 1.3%) i/s - 28.946M in 5.070660s fast no-arg 6.892M (± 0.7%) i/s - 34.632M in 5.024961s Comparison: fast no-arg: 6892287.7 i/s orig no-arg: 5709450.0 i/s - 1.21x (± 0.00) slower Warming up -------------------------------------- orig :human 575.000 i/100ms fast :human 619.000 i/100ms Calculating ------------------------------------- orig :human 6.176k (± 1.6%) i/s - 31.050k in 5.028656s fast :human 6.179k (± 1.8%) i/s - 30.950k in 5.010372s Comparison: fast :human: 6179.1 i/s orig :human: 6176.3 i/s - same-ish: difference falls within error ``` --- .../lib/active_support/core_ext/numeric/conversions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/numeric/conversions.rb b/activesupport/lib/active_support/core_ext/numeric/conversions.rb index 3e623e0d177..1e67e7c7e7d 100644 --- a/activesupport/lib/active_support/core_ext/numeric/conversions.rb +++ b/activesupport/lib/active_support/core_ext/numeric/conversions.rb @@ -107,9 +107,9 @@ module ActiveSupport # separator: ',', # significant: false) # => "1,2 Million" def to_s(format = nil, options = nil) + return super() if format.nil? + case format - when nil - super() when Integer, String super(format) when :phone