Skip to content

Instantly share code, notes, and snippets.

@postmodern
Last active July 21, 2021 03:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save postmodern/92afa434b31107e41ea76f73bf7b0160 to your computer and use it in GitHub Desktop.
Save postmodern/92afa434b31107e41ea76f73bf7b0160 to your computer and use it in GitHub Desktop.
Benchmark of the String#gsub! based Inflector.camelize method vs. a StringScanner based method
#!/usr/bin/env ruby
require 'benchmark'
require 'strscan'
module Inflector
def self.gsub_camelize(name)
name = name.to_s.dup
# sourced from: https://github.com/dry-rb/dry-inflector/blob/c918f967ff82611da374eb0847a77b7e012d3fa8/lib/dry/inflector.rb#L329-L334
name.sub!(/^[a-z\d]*/,&:capitalize)
name.gsub!(%r{(?:[_-]|(/))([a-z\d]*)}i) do |match|
slash = Regexp.last_match(1)
word = Regexp.last_match(2)
"#{slash}#{word.capitalize}"
end
name.gsub!('/','::')
name
end
def self.strscan_camelize(name)
scanner = StringScanner.new(name.to_s)
new_string = String.new
until scanner.eos?
if (word = scanner.scan(/[a-z\d]+/i))
new_string << word.capitalize
elsif scanner.scan(/[_-]+/)
next
elsif scanner.scan(/\//)
new_string << '::'
end
end
new_string
end
end
Benchmark.bm(12) do |b|
n = 1_009_000
underscored = "foo_bar/baz_quix"
b.report('Inflector.camelize + String#gsub!') do
n.times do
Inflector.gsub_camelize(underscored)
end
end
b.report('Inflector.camelize + StringScanner') do
n.times do
Inflector.strscan_camelize(underscored)
end
end
end
#!/usr/bin/env ruby
require 'benchmark'
require 'strscan'
module Inflector
def self.gsub_underscore(name)
# sourced from: https://github.com/dry-rb/dry-inflector/blob/c918f967ff82611da374eb0847a77b7e012d3fa8/lib/dry/inflector.rb#L286-L287
name = name.to_s.dup
name.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
name.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
name.tr!('-','_')
name.downcase!
name
end
def self.strscan_underscore(name)
scanner = StringScanner.new(name.to_s)
words = []
until scanner.eos?
if (capitalized = scanner.scan(/[A-Z][a-z\d]+/))
words << capitalized
elsif (uppercase = scanner.scan(/[A-Z][A-Z\d]*(?=[A-Z]|$)/))
words << uppercase
elsif (lowercase = scanner.scan(/[a-z][a-z\d]*/))
words << lowercase
elsif scanner.scan(/[_-]+/)
next
else
raise(ArgumentError,"string contains invalid characters: #{scanner.string}")
end
end
words.join('_').downcase
end
end
Benchmark.bm(12) do |b|
n = 1_009_000
underscored = "FooBarBazQux"
b.report('Inflector.underscore + String#gsub!') do
n.times do
Inflector.gsub_underscore(underscored)
end
end
b.report('Inflector.underscore + StringScanner') do
n.times do
Inflector.strscan_underscore(underscored)
end
end
end
@postmodern
Copy link
Author

postmodern commented Jul 20, 2021

Output:

                   user     system      total        real
Inflector.camelize + String#gsub!  7.023820   0.001943   7.025763 (  7.066990)
Inflector.camelize + StringScanner  4.646641   0.000993   4.647634 (  4.681039)
                   user     system      total        real
Inflector.underscore + String#gsub!  5.733684   0.002956   5.736640 (  5.791869)
Inflector.underscore + StringScanner  2.842114   0.000000   2.842114 (  2.862411)

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