Many articles and examples of string concatenation for Ruby will show the use of the + and += operators that many of us are familiar with from other programming languages.
"ruby" + "-performance" # => "ruby-performance"
s = "ruby" s += "-performance" # => "ruby-performance"
Those operators serve their purpose but look closer and you’ll see within the Ruby documentation that the + operator returns a new object, meaning that there is a performance overhead as it instantiates a new String instance. That makes these operators a poor choice from both a time and memory perspective particularly when performing repeated concatenation.
A far better option is the « operator which instead returns the same String instance concatenated with the given string.
s = "ruby" s << "-performance" # => "ruby-performance"
The performance benefit from using the « operator was very apparent in an application we worked on that used raw SQL to update multiple records with a single SQL statement using UPDATE FROM and a VALUES list. The Ruby code to build the VALUES list iterated through the objects in memory concatenating a string with the column values to update. Changing this code to concatenate the string using the « operator rather than the += operator resulted in a significant improvement in the execution time of the method.
The difference in timing and memory allocation can be seen by running the following benchmark scripts.
Save the following content as “concat_bt.rb” then in the terminal run ruby concat_bt.rb
require "benchmark" str_plus = "" str_shovel = "" str = "a" n = 1000 Benchmark.bm do |benchmark| benchmark.report("string +=") do n.times do |n| str_plus += str end end benchmark.report("string <<") do n.times do str_shovel << str end end end
The benchmark output shows that the « operator is almost three times faster than the += operator; and that performance gain increases with a greater number of iterations.
user system total real string += 0.000045 0.000005 0.000050 ( 0.000045) string << 0.000016 0.000000 0.000016 ( 0.000017)
Using the benchmark-memory gem, we can also benchmark the memory usage of the two methods. Save the following content as “concat_bm.rb”, then in the terminal first run gem install benchmark-memory then run ruby concat_bm.rb
require "benchmark/memory" str_plus = "" str_shovel = "" str = "a" n = 1000 Benchmark.memory do |benchmark| benchmark.report("string +=") do n.times do |n| str_plus += str end end benchmark.report("string <<") do n.times do str_shovel << str end end benchmark.compare! end
The benchmark-memory output shows the memory and objects allocated by the += and « operators - this clearly shows many new objects being created and memory consumed when using the += operator and none when using the « operator.
string += 541.201k memsize ( 1.041k retained) 1.000k objects ( 1.000 retained) 50.000 strings ( 1.000 retained) string << 0.000 memsize ( 0.000 retained) 0.000 objects ( 0.000 retained) 0.000 strings ( 0.000 retained) Comparison: string <<: 0 allocated string +=: 541201 allocated - Infx more
Get in touch
Looking to improve your Ruby performance and understand the benefits it will bring? In our 25 years of experience building and maintaining software applications, we’ve learnt a fair bit along the way. If you are after some advice or want to outsource the task in front of you, contact us today on firstname.lastname@example.org or via our contact form, we’d love to hear about it.