Optimize ERB string concat using opt_ltlt #1612

wants to merge 1 commit into


1 participant

k0kubun commented May 13, 2017

Using opt_ltlt instruction instead of opt_send_without_block for #concat, we can bypass method call and use rb_str_concat directly. So I changed ERB to generate #<<.


With trunk ruby and compiled result of bm_app_erb.rb's erb template like this,

require 'benchmark/ips'

Benchmark.ips do |x|
  title = "hello world!"
  content = "hello world!\n" * 10

  x.report('concat') do
    _erbout = String.new; _erbout.concat "<html>\n  <head> "
    ; _erbout.concat(( title ).to_s); _erbout.concat " </head>\n  <body>\n    <h1> "
    ; _erbout.concat(( title ).to_s); _erbout.concat " </h1>\n    <p>\n      "
    ; _erbout.concat(( content ).to_s); _erbout.concat "\n    </p>\n  </body>\n</html>\n"
    ; _erbout.force_encoding(__ENCODING__)
  x.report('<<') do
    _erbout = String.new; _erbout.<< "<html>\n  <head> "
    ; _erbout.<<(( title ).to_s); _erbout.<< " </head>\n  <body>\n    <h1> "
    ; _erbout.<<(( title ).to_s); _erbout.<< " </h1>\n    <p>\n      "
    ; _erbout.<<(( content ).to_s); _erbout.<< "\n    </p>\n  </body>\n</html>\n"
    ; _erbout.force_encoding(__ENCODING__)

the rendering performance benchmark result is:

Calculating -------------------------------------
              concat    301.067k (± 9.1%) i/s -      1.510M in   5.056566s
                  <<    533.025k (±11.3%) i/s -      2.654M in   5.042675s

                  <<:   533024.6 i/s
              concat:   301066.7 i/s - 1.77x  slower

@hsbt hsbt closed this in 52c7384 May 15, 2017

@k0kubun k0kubun deleted the k0kubun:erb-opt-ltlt branch May 15, 2017

pocke added a commit to pocke/brakeman that referenced this pull request Dec 22, 2017

Make ErbTemplateProcessor aware of `String#<<` method for Ruby 2.5
Currently, the test fails on Ruby 2.5.

$ ruby -v
ruby 2.5.0rc1 (2017-12-14 trunk 61243) [x86_64-linux]
$ bundle exec rake
Run options: --seed 24396

[1m[32mNo warnings found[0m


..Unrecognized action on _erbout: <<
Coverage report generated for Unit Tests to /home/pocke/ghq/github.com/presidentbeef/brakeman/coverage. 6542 / 11879 LOC (55.07%) covered.
rake aborted!
Command failed with status (1): [ruby -I"lib" -I"/home/pocke/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/rake-10.1.1/lib" "/home/pocke/.rbenv/versions/2.5.0-rc1/lib/ruby/gems/2.5.0/gems/rake-10.1.1/lib/rake/rake_test_loader.rb" "test/tests/*.rb" ]

Tasks: TOP => default => test
(See full trace by running task with --trace)

Because ERB uses `String#<<` instead of `concat` in Ruby 2.5.
See ruby/ruby#1612

This pull-request will fix the problem.

@pocke pocke referenced this pull request in presidentbeef/brakeman Dec 22, 2017


Make ErbTemplateProcessor aware of `String#<<` method for Ruby 2.5 #1149

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