Last active
July 12, 2021 07:13
-
-
Save postmodern/b664b4a34ea7c1f2598cde04cb5bff5b to your computer and use it in GitHub Desktop.
Ruby String building benchmarks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'benchmark' | |
require 'zlib' | |
Benchmark.bm(12) do |b| | |
n = 10_009_000 | |
str1 = 'A' | |
str2 = 'A' | |
str3 = 'A' | |
str4 = 'A' | |
str5 = 'A' | |
str6 = 'A' | |
str7 = 'A' | |
str8 = 'A' | |
str9 = 'A' | |
str10 = 'A' | |
b.report('interpolated') do | |
n.times do | |
"#{str1} #{str2} #{str3} #{str4} #{str5} #{str6} #{str7} #{str8} #{str9} #{str10}" | |
end | |
end | |
b.report('String#<<') do | |
n.times do | |
buffer = String.new('', capacity: 19) | |
buffer << str1 << ' ' << str2 << ' ' << str3 << ' ' << str4 << ' ' << str5 << ' ' << str6 << ' ' << str7 << ' ' << str8 << ' ' << str9 << ' ' << str10 | |
end | |
end | |
b.report('String#[]=') do | |
n.times do | |
buffer = ' ' * 19 | |
buffer[0,1] = str1 | |
buffer[1,1] = str2 | |
buffer[2,1] = str3 | |
buffer[3,1] = str4 | |
buffer[4,1] = str5 | |
buffer[5,1] = str6 | |
buffer[6,1] = str7 | |
buffer[7,1] = str8 | |
buffer[8,1] = str9 | |
buffer[9,1] = str10 | |
end | |
end | |
b.report('Array#join') do | |
n.times do | |
[str1, str2, str3, str4, str5, str6, str7, str8, str9, str10].join(' ') | |
end | |
end | |
end |
Author
postmodern
commented
Jul 11, 2021
Interesting!
If you have this code:
"a #{b} c"
and see what bytecode Ruby produces for it:
puts RubyVM::InstructionSequence.compile(%("a \#{b} c")).disassemble
You get this:
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,10)> (catch: FALSE)
0000 putobject "a " ( 1)[Li]
0002 putself
0003 opt_send_without_block <calldata!mid:b, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0005 dup
0006 checktype T_STRING
0008 branchif 14
0010 dup
0011 opt_send_without_block <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0013 tostring
0014 putobject " c"
0016 concatstrings 3
0018 leave
So it pushes strings to the VM stack, then executes the concatstrings
instruction, which is defined here:
https://github.com/ruby/ruby/blob/c2ed5ab08b0f508185b4abd2d28f045616e7c7f5/insns.def#L372-L383
It's still strange that String.new
combined with #<<
is much slower, maybe <<
does some checks to see the types, or something like that.
and the results of running the benchmarks under benchmark-ips.
Warming up --------------------------------------
interpolated 1.000 i/100ms
String#<< 1.000 i/100ms
String#[]= 1.000 i/100ms
Array#join 1.000 i/100ms
Calculating -------------------------------------
interpolated 0.109 (± 0.0%) i/s - 1.000 in 9.209206s
String#<< 0.045 (± 0.0%) i/s - 1.000 in 22.043854s
String#[]= 0.076 (± 0.0%) i/s - 1.000 in 13.169221s
Array#join 0.071 (± 0.0%) i/s - 1.000 in 14.087317s
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment