Software Development

Ruby Performance Tips - String Concatenation


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


icon image

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 hello@circle-sd.com or via our contact form, we’d love to hear about it.