How to quickly join two strings in Ruby How to quickly join two strings in Ruby ruby ruby

How to quickly join two strings in Ruby


Starting with short strings:

z = 'z''a ' << z      # => "a z"'a '.concat(z) # => "a z"'a ' + z       # => "a z""a #{z}"       # => "a z"require 'fruity'compare do  append      { 'a ' << z}  concat      { 'a '.concat(z)}  plus        { 'a ' + z}  interpolate { "a #{z}" }end# >> Running each test 65536 times. Test will take about 2 seconds.# >> interpolate is similar to append# >> append is similar to plus# >> plus is faster than concat by 2x ± 0.1

Increasing the "left" string to 11 characters:

require 'fruity'compare do  append      { 'abcdefghij ' << z}  concat      { 'abcdefghij '.concat(z)}  plus        { 'abcdefghij ' + z}  interpolate { "abcdefghij #{z}" }end# >> Running each test 65536 times. Test will take about 2 seconds.# >> interpolate is similar to append# >> append is similar to plus# >> plus is faster than concat by 2x ± 1.0

51 characters:

compare do  append      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' << z}  concat      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij '.concat(z)}  plus        { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' + z}  interpolate { "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij #{z}" }end# >> Running each test 32768 times. Test will take about 2 seconds.# >> plus is faster than append by 2x ± 1.0# >> append is similar to interpolate# >> interpolate is similar to concat

101:

compare do  append      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' << z}  concat      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij '.concat(z)}  plus        { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' + z}  interpolate { "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij #{z}" }end# >> Running each test 32768 times. Test will take about 2 seconds.# >> plus is faster than interpolate by 2x ± 0.1# >> interpolate is similar to append# >> append is similar to concat

501:

compare do  append      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' << z}  concat      { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij '.concat(z)}  plus        { 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij ' + z}  interpolate { "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij #{z}" }end# >> Running each test 16384 times. Test will take about 1 second.# >> plus is faster than append by 2x ± 0.1# >> append is similar to interpolate# >> interpolate is similar to concat

Once the strings got past 50 characters + consistently outperformed the others.

In the comments there are mention of some of these mutating the string on the left. Here's what would happen if it was a variable on the left, not a literal string:

a = 'a'z = 'z'a << z # => "az"a # => "az"a = 'a'a.concat(z) # => "az"a # => "az"

compared to:

a + z # => "az"a     # => "a""#{a} #{z}" # => "a z"a           # => "a"

Note: The initial version of answer had a bad test using:

"a #{'z'}"

The problem with that is Ruby is smart enough to recognize that 'z' is another literal and converts the string into:

"a z"

with the end result that the test would be unfairly faster than the others.


It's been a while and Ruby's smarter and faster. I added a couple additional tests:

puts "Running Ruby v%s" % RUBY_VERSIONrequire 'fruity'z_ = 'z'compare do  append       { 'abcdefghij ' << 'z'      }  concat       { 'abcdefghij '.concat('z') }  plus         { 'abcdefghij ' + 'z'       }  interpolate1 { "abcdefghij #{'z'}"       }  interpolate2 { "abcdefghij #{z_}"        }  adjacent     { 'abcdefghij' ' z'         }end# >> Running Ruby v2.7.0# >> Running each test 65536 times. Test will take about 3 seconds.# >> adjacent is similar to interpolate1# >> interpolate1 is faster than interpolate2 by 2x ± 1.0# >> interpolate2 is similar to append# >> append is similar to concat# >> concat is similar to plus

interpolate1 and adjacent are basically the same as far as the interpreter is concerned and will be concatenated prior to running. interpolate2 forces Ruby to do it at run-time so it's a little slower.