Benchmarking Ruby String Conversion

24 January 2012

I have a class (Parslet::Slice, to be precise) that inherits from String but needs to be converted back to a string. Which is faster, String#to_s or String()? My hunch is the latter, but I can find out for sure in about a minute:

#!/usr/bin/env ruby
require 'benchmark'

TIMES_TO_REPEAT = 10_000_000

class SpecialString < String; end

Benchmark.bmbm do|b|
  b.report("''.to_s") do
    str = ''
    TIMES_TO_REPEAT.times { str.to_s }
  end
  
  b.report('"".to_s') do
    str = ""
    TIMES_TO_REPEAT.times { str.to_s }
  end
  
  b.report("String(str)") do
    str = SpecialString.new("")
    TIMES_TO_REPEAT.times { String(str) }
  end

  b.report("str.to_s") do
    str = SpecialString.new("")
    TIMES_TO_REPEAT.times { str.to_s }
  end
end

With Ruby 1.9:

ruby which_string_coercion_is_faster.rb ↵
Rehearsal -----------------------------------------------
''.to_s       0.970000   0.000000   0.970000 (  0.968759)
"".to_s       0.960000   0.000000   0.960000 (  0.960182)
String(str)   0.920000   0.000000   0.920000 (  0.920623)
str.to_s      2.250000   0.010000   2.260000 (  2.249877)
-------------------------------------- total: 5.110000sec

                  user     system      total        real
''.to_s       0.930000   0.000000   0.930000 (  0.931371)
"".to_s       0.940000   0.000000   0.940000 (  0.934964)
String(str)   0.910000   0.000000   0.910000 (  0.913435)
str.to_s      2.250000   0.010000   2.260000 (  2.252326)

Similar results on 1.8:

Rehearsal -----------------------------------------------
''.to_s       1.470000   0.000000   1.470000 (  1.475513)
"".to_s       1.540000   0.000000   1.540000 (  1.540667)
String(str)   1.510000   0.000000   1.510000 (  1.509818)
str.to_s      2.710000   0.000000   2.710000 (  2.713870)
-------------------------------------- total: 7.230000sec

                  user     system      total        real
''.to_s       1.500000   0.000000   1.500000 (  1.497413)
"".to_s       1.520000   0.000000   1.520000 (  1.512449)
String(str)   1.540000   0.000000   1.540000 (  1.536261)
str.to_s      2.690000   0.000000   2.690000 (  2.700433)