Skip to content

Optimize underscore inflector#41303

Merged
kaspth merged 1 commit into
rails:mainfrom
Shopify:underscore-allocations
Feb 2, 2021
Merged

Optimize underscore inflector#41303
kaspth merged 1 commit into
rails:mainfrom
Shopify:underscore-allocations

Conversation

@casperisfine

Copy link
Copy Markdown
Contributor

Followup: #41296

Uses the same principle to optimize the method, we combine two gsub! into one.

There one extra detail though, I didn't realize $1 would return a new instance on every reference. So we store it in a variable when we access these twice or more. I applied the same change to camelize too.

The gains only materialize in specific cases, in others there's pretty much no difference.

require 'benchmark/ips'
require 'active_support/all'

ActiveSupport::Inflector.inflections do |inflect|
    inflect.acronym('RESTful')
end

module ActiveSupport
  module Inflector
    def underscore2(camel_cased_word)
      return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
      word = camel_cased_word.to_s.gsub("::", "/")
      word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
      word.gsub!(/([A-Z\d]+)([A-Z][a-z])|([a-z\d])([A-Z])/) do
        if first_match = $1
          first_match << '_' << $2
        else
          $3 << '_' << $4
        end
      end
      word.tr!("-", "_")
      word.downcase!
      word
    end
  end
end

%w(RESTfulController Foo FooBar Foo::BarBaz FOOBar::EggSpam).each do |str|
  puts "== Comparing with #{str.inspect} (#{RUBY_VERSION}) =="
  unless ActiveSupport::Inflector.underscore(str) == ActiveSupport::Inflector.underscore2(str)
    raise "#{ActiveSupport::Inflector.underscore2(str)} != #{ActiveSupport::Inflector.underscore(str)}"
  end

  Benchmark.ips do |x|
    x.report('underscore') { ActiveSupport::Inflector.underscore(str) }
    x.report('underscore2') { ActiveSupport::Inflector.underscore2(str) }
    x.compare!
  end
  puts
end
== Comparing with "RESTfulController" (2.7.2) ==
Warming up --------------------------------------
          underscore    16.527k i/100ms
         underscore2    16.118k i/100ms
Calculating -------------------------------------
          underscore    165.223k (± 0.9%) i/s -    826.350k in   5.001882s
         underscore2    163.251k (± 0.9%) i/s -    822.018k in   5.035736s

Comparison:
          underscore:   165222.7 i/s
         underscore2:   163250.9 i/s - same-ish: difference falls within error

== Comparing with "Foo" (2.7.2) ==
Warming up --------------------------------------
          underscore    66.965k i/100ms
         underscore2    72.961k i/100ms
Calculating -------------------------------------
          underscore    669.221k (± 0.8%) i/s -      3.348M in   5.003557s
         underscore2    729.815k (± 1.1%) i/s -      3.721M in   5.099188s

Comparison:
         underscore2:   729815.0 i/s
          underscore:   669221.3 i/s - 1.09x  (± 0.00) slower

== Comparing with "FooBar" (2.7.2) ==
Warming up --------------------------------------
          underscore    26.869k i/100ms
         underscore2    26.446k i/100ms
Calculating -------------------------------------
          underscore    268.071k (± 0.7%) i/s -      1.343M in   5.011813s
         underscore2    264.400k (± 0.6%) i/s -      1.322M in   5.001317s

Comparison:
          underscore:   268071.4 i/s
         underscore2:   264400.1 i/s - 1.01x  (± 0.00) slower

== Comparing with "Foo::BarBaz" (2.7.2) ==
Warming up --------------------------------------
          underscore    20.611k i/100ms
         underscore2    20.609k i/100ms
Calculating -------------------------------------
          underscore    205.671k (± 1.3%) i/s -      1.031M in   5.011478s
         underscore2    204.304k (± 1.2%) i/s -      1.030M in   5.044446s

Comparison:
          underscore:   205670.8 i/s
         underscore2:   204304.1 i/s - same-ish: difference falls within error

== Comparing with "FOOBar::EggSpam" (2.7.2) ==
Warming up --------------------------------------
          underscore    15.348k i/100ms
         underscore2    18.932k i/100ms
Calculating -------------------------------------
          underscore    154.686k (± 0.9%) i/s -    782.748k in   5.060656s
         underscore2    188.706k (± 0.8%) i/s -    946.600k in   5.016580s

Comparison:
         underscore2:   188705.6 i/s
          underscore:   154685.7 i/s - 1.22x  (± 0.00) slower

@kaspth @rafaelfranca @etiennebarrie

```ruby
require 'benchmark/ips'
require 'active_support/all'

ActiveSupport::Inflector.inflections do |inflect|
    inflect.acronym('RESTful')
end

module ActiveSupport
  module Inflector
    def underscore2(camel_cased_word)
      return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
      word = camel_cased_word.to_s.gsub("::", "/")
      word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
      word.gsub!(/([A-Z\d]+)([A-Z][a-z])|([a-z\d])([A-Z])/) do
        if first_match = $1
          first_match << '_' << $2
        else
          $3 << '_' << $4
        end
      end
      word.tr!("-", "_")
      word.downcase!
      word
    end
  end
end

%w(RESTfulController Foo FooBar Foo::BarBaz FOOBar::EggSpam).each do |str|
  puts "== Comparing with #{str.inspect} (#{RUBY_VERSION}) =="
  unless ActiveSupport::Inflector.underscore(str) == ActiveSupport::Inflector.underscore2(str)
    raise "#{ActiveSupport::Inflector.underscore2(str)} != #{ActiveSupport::Inflector.underscore(str)}"
  end

  Benchmark.ips do |x|
    x.report('underscore') { ActiveSupport::Inflector.underscore(str) }
    x.report('underscore2') { ActiveSupport::Inflector.underscore2(str) }
    x.compare!
  end
  puts
end
```

```
== Comparing with "RESTfulController" (2.7.2) ==
Warming up --------------------------------------
          underscore    16.527k i/100ms
         underscore2    16.118k i/100ms
Calculating -------------------------------------
          underscore    165.223k (± 0.9%) i/s -    826.350k in   5.001882s
         underscore2    163.251k (± 0.9%) i/s -    822.018k in   5.035736s

Comparison:
          underscore:   165222.7 i/s
         underscore2:   163250.9 i/s - same-ish: difference falls within error

== Comparing with "Foo" (2.7.2) ==
Warming up --------------------------------------
          underscore    66.965k i/100ms
         underscore2    72.961k i/100ms
Calculating -------------------------------------
          underscore    669.221k (± 0.8%) i/s -      3.348M in   5.003557s
         underscore2    729.815k (± 1.1%) i/s -      3.721M in   5.099188s

Comparison:
         underscore2:   729815.0 i/s
          underscore:   669221.3 i/s - 1.09x  (± 0.00) slower

== Comparing with "FooBar" (2.7.2) ==
Warming up --------------------------------------
          underscore    26.869k i/100ms
         underscore2    26.446k i/100ms
Calculating -------------------------------------
          underscore    268.071k (± 0.7%) i/s -      1.343M in   5.011813s
         underscore2    264.400k (± 0.6%) i/s -      1.322M in   5.001317s

Comparison:
          underscore:   268071.4 i/s
         underscore2:   264400.1 i/s - 1.01x  (± 0.00) slower

== Comparing with "Foo::BarBaz" (2.7.2) ==
Warming up --------------------------------------
          underscore    20.611k i/100ms
         underscore2    20.609k i/100ms
Calculating -------------------------------------
          underscore    205.671k (± 1.3%) i/s -      1.031M in   5.011478s
         underscore2    204.304k (± 1.2%) i/s -      1.030M in   5.044446s

Comparison:
          underscore:   205670.8 i/s
         underscore2:   204304.1 i/s - same-ish: difference falls within error

== Comparing with "FOOBar::EggSpam" (2.7.2) ==
Warming up --------------------------------------
          underscore    15.348k i/100ms
         underscore2    18.932k i/100ms
Calculating -------------------------------------
          underscore    154.686k (± 0.9%) i/s -    782.748k in   5.060656s
         underscore2    188.706k (± 0.8%) i/s -    946.600k in   5.016580s

Comparison:
         underscore2:   188705.6 i/s
          underscore:   154685.7 i/s - 1.22x  (± 0.00) slower
```
@casperisfine casperisfine force-pushed the underscore-allocations branch from 59d44c6 to 58147cd Compare February 2, 2021 10:46
@kaspth kaspth merged commit a2a5385 into rails:main Feb 2, 2021
@kaspth

kaspth commented Feb 2, 2021

Copy link
Copy Markdown
Contributor

Thanks for working on these!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants