Some things to keep in mind:
- captures can be slow, so if you can avoid them, you can get better
performance
- looping using "for" is usually faster than using "each { ... }",
because it doesn't have to create a new scope every time the block
is called
Three alternative solutions added given the above (my computer is slower
than yours, so I reduced the number of iterations too). Interesting
that I was able to write an enumerator class in ruby that was faster
than the builtin enumerator class (which is written in C). Also
interesting is how much slower the enumerator solution is on YARV:
require 'benchmark'
require 'enumerator'
class EachByteEnumerator
def initialize(obj)
@obj = obj
end
def each(&block)
@obj.each_byte(&block)
end
end
Benchmark.bmbm do |b|
s = "abc" * 100_000
re = /(.)/
b.report("s.gsub(re) {|x| x*2}") do
s.gsub(re) {|x| x*2}
end
b.report("s.gsub(re, '\\1\\1')") do
s.gsub(re, '\1\1')
end
b.report("s.gsub(/./, '\\0\\0')") do
s.gsub(/./, '\0\0')
end
b.report("7stud1") do
new_str = ""
s.each_byte do |byte|
2.times do
new_str << byte
end
end
end
b.report("7stud2") do
new_str = ""
s.each_byte do |byte|
new_str << byte << byte
end
end
b.report("7stud_enum") do
new_str = ""
o = s.to_enum(:each_byte)
for byte in o do
new_str << byte << byte
end
end
b.report("7stud_enum2") do
new_str = ""
o = EachByteEnumerator.new(s)
for byte in o do
new_str << byte << byte
end
end
end
[pbrannan@zaphod tmp]$ ruby test.rb
Rehearsal --------------------------------------------------------
s.gsub(re) {|x| x*2} 2.410000 0.010000 2.420000 ( 2.431675)
s.gsub(re, '\1\1') 0.900000 0.140000 1.040000 ( 1.035942)
s.gsub(/./, '\0\0') 0.840000 0.070000 0.910000 ( 0.919175)
7stud1 1.430000 0.000000 1.430000 ( 1.430979)
7stud2 0.580000 0.010000 0.590000 ( 0.585925)
7stud_enum 0.820000 0.010000 0.830000 ( 0.837720)
7stud_enum2 0.510000 0.000000 0.510000 ( 0.511776)
----------------------------------------------- total: 7.730000sec
user system total real
s.gsub(re) {|x| x*2} 2.450000 0.000000 2.450000 ( 2.455824)
s.gsub(re, '\1\1') 0.860000 0.010000 0.870000 ( 0.861705)
s.gsub(/./, '\0\0') 0.820000 0.000000 0.820000 ( 0.818279)
7stud1 1.460000 0.010000 1.470000 ( 1.483312)
7stud2 0.590000 0.000000 0.590000 ( 0.589141)
7stud_enum 0.820000 0.000000 0.820000 ( 0.817231)
7stud_enum2 0.520000 0.000000 0.520000 ( 0.514009)
[pbrannan@zaphod tmp]$ ruby1.9 test.rb
Rehearsal --------------------------------------------------------
s.gsub(re) {|x| x*2} 2.880000 0.030000 2.910000 ( 2.961747)
s.gsub(re, '\1\1') 1.850000 0.120000 1.970000 ( 1.972712)
s.gsub(/./, '\0\0') 1.770000 0.040000 1.810000 ( 1.815295)
7stud1 1.040000 0.000000 1.040000 ( 1.056692)
7stud2 0.640000 0.000000 0.640000 ( 0.660844)
7stud_enum 1.710000 0.080000 1.790000 ( 1.786316)
7stud_enum2 1.640000 0.120000 1.760000 ( 1.770983)
---------------------------------------------- total: 11.920000sec
user system total real
s.gsub(re) {|x| x*2} 2.950000 0.030000 2.980000 ( 2.999795)
s.gsub(re, '\1\1') 1.910000 0.030000 1.940000 ( 1.938242)
s.gsub(/./, '\0\0') 1.760000 0.030000 1.790000 ( 1.811339)
7stud1 1.040000 0.000000 1.040000 ( 1.055916)
7stud2 0.650000 0.030000 0.680000 ( 0.685329)
7stud_enum 1.520000 0.120000 1.640000 ( 1.661215)
7stud_enum2 1.450000 0.120000 1.570000 ( 1.590461)
[pbrannan@zaphod tmp]$ ruby1.9 -v
ruby 1.9.0 (2008-01-24 revision 0) [i686-linux]