Feature #4115: Faster ERB patch
http://redmine.ruby-lang.org/issues/show/4115

Author: Simon Chiang
Status: Open, Priority: Normal

ERB can be made faster by pushing all string fragments onto an array and then performing one join to make the result, rather than concatenating each string fragment.  This illustrates the patch:

  [erb_benchmark.rb]
  require 'benchmark'
  require 'erb'

  class FasterERB < ERB
    def set_eoutvar(compiler, eoutvar = '_erbout')
      compiler.put_cmd = "#{eoutvar}<<"
      compiler.insert_cmd = "#{eoutvar}<<"

      cmd = []
      cmd.push "#{eoutvar} = []"
  
      compiler.pre_cmd = cmd

      cmd = []
      cmd.push "#{eoutvar}.join"

      compiler.post_cmd = cmd
    end
  end

  Benchmark.bm(30) do |x|
    n = 10
    template = %q{got <%= "value" %> }

    erb  = ERB.new template
    frb = FasterERB.new template

    x.report "#{n}k small" do
      (1000 * n).times { erb.result }
    end

    x.report "#{n}k small (fast)" do
      (1000 * n).times { frb.result }
    end

    erb = ERB.new(template * 100)
    frb = FasterERB.new(template * 100)

    x.report "#{n}k large" do
      (1000 * n).times { erb.result }
    end

    x.report "#{n}k large (fast)" do
      (1000 * n).times { frb.result }
    end
    
    if erb.result == frb.result
      puts "outputs are equal"
    end
  end

As an example:

  % ruby --version
  ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]
  % ruby erb_benchmark.rb 
                            user     system      total        real
  10k small             0.170000   0.000000   0.170000 (  0.163556)
  10k small (fast)      0.150000   0.000000   0.150000 (  0.153408)
  10k large             6.560000   0.020000   6.580000 (  6.579351)
  10k large (fast)      5.690000   0.010000   5.700000 (  5.703097)
  outputs are equal

I attempted to make up a proper patch but I'm unfamiliar with the repository.  I wasn't sure how to run the tests, etc.  Obviously the patch simply consists of replacing the set_eoutvar method.

Note that on 1.9.3 it looks like you would also have to force the encoding of the final string.

  cmd.push "#{eoutvar}.join.force_encoding(__ENCODING__)"


----------------------------------------
http://redmine.ruby-lang.org