Optimize ActiveSupport::NumericWithFormat#to_s

`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

```
This commit is contained in:
Jean Boussier 2021-03-30 15:41:20 +02:00
parent 6675f6b785
commit 31c20e248a
1 changed files with 2 additions and 2 deletions

View File

@ -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