Few days ago, guys from Netflix released new gem - https://github.com/Netflix/fast_jsonapi.
I’ve started implementation of one feature for this gem, and found interesting spec tests. For example some serialization methods should work at least 25 times faster than AMS. Interesting case!
After some investigation, I found for example - what simple call :symbol.to_s.pluralize takes a lot of time, and broke some test cases.
So, I decided to sort it out, and added some benchmarks.
My current ruby version:
$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin17]
Let’s start from string and this benchmark. Concatenation works faster, because we just modify existing string object. Without creation temp string object as for example for “+” operation.
require 'benchmark'
n = 100000
Benchmark.bm do |x|
var1 = "One"
var2 = "Two"
x.report ('interpolation') { n.times do ; "Interpolation of #{var1} and #{var2} through interpolation" ; end }
x.report ('plus') { n.times do ; "Adding of " + var1 + " and " + var2 ; end }
x.report ('concatenation') { n.times do ; "Concatenation of " << var1 << " and " << var2 ; end }
end
# user system total real
# interpolation 0.060000 0.010000 0.070000 ( 0.071697)
# plus 0.100000 0.000000 0.100000 ( 0.105198)
# concatenation 0.050000 0.000000 0.050000 ( 0.045616)
Interesting and obvious thing here - when we use directly same data structure, code runs faster. You can read about bang! methods here.
require 'benchmark'
def merge!(array)
array.inject({}) { |h, e| h.merge!(e => e) }
end
def merge(array)
array.inject({}) { |h, e| h.merge(e => e) }
end
N = 10_000
array = (0..N).to_a
Benchmark.bm(10) do |x|
x.report("merge!") { merge!(array) }
x.report("merge") { merge(array) }
end
# user system total real
# merge! 0.010000 0.000000 0.010000 ( 0.009320)
# merge 1.760000 0.570000 2.330000 ( 2.365817)
When we trying to access instance variable, it works about two times faster! Accessor methods are too slow.
require 'benchmark'
class Tester
attr_accessor :test_var
def initialize(count)
@count = count
@test_var = 22
end
def benchmark
Benchmark.bm(10) do |x|
x.report("@test_var") { @count.times { @test_var } }
x.report("test_var" ) { @count.times { test_var } }
x.report("@test_var =") { @count.times {|i| @test_var = i } }
x.report("self.test_var =") { @count.times {|i| self.test_var = i } }
end
end
end
metric = Tester.new(100_000_000)
metric.benchmark
# user system total real
# @test_var 4.080000 0.050000 4.130000 ( 4.481426)
# test_var 4.560000 0.030000 4.590000 ( 4.858760)
# @test_var = 4.500000 0.030000 4.530000 ( 4.703120)
# self.test_var = 5.150000 0.040000 5.190000 ( 5.512396)
Last interesting thing in this post - parallel assigment. Yep - it’s slow :)
require 'benchmark'
COUNT = 10_000_000
Benchmark.bm(15) do |run|
run.report('parallel') do
COUNT.times do
x, y = 100, 200
end
end
run.report('standart') do |run|
COUNT.times do
x = 100
y = 200
end
end
end
# user system total real
# parallel 0.770000 0.000000 0.770000 ( 0.784675)
# standart 0.450000 0.010000 0.460000 ( 0.460964)