Using this code:
include Benchmark
n = 10**8
r = []
Benchmark.benchmark(" "*7 + CAPTION, 7, FORMAT, ">avg:", ">total:") do |b|
a = 1
r << b.report("Benchmark -1:") { (n).times do
-1 * a
end }
r << b.report("Benchmark - :") { (n).times do
-a
end }
[(r.sum{|e| e }) / 2, r.sum{|e| e }]
end
I get this result back on ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]
:
user system total real
Benchmark -1: 4.930000 0.000000 4.930000 ( 4.938359)
Benchmark - : 5.650000 0.000000 5.650000 ( 5.667566)
>avg: 5.290000 0.000000 5.290000 ( 5.302962)
>total: 10.580000 0.000000 10.580000 ( 10.605924)
That looks counter intuitive, because if I would guess I would bet on "-x" not "-1*x" to be faster.
Why the difference? Or do I have some major flaw in my measurement?
It might help if you look at the generated bytecode. If you reduce it to something simple like
puts RubyVM::InstructionSequence.compile(<<-CODE
a = 1
-1 * a
CODE
).to_a.join("\n")
puts RubyVM::InstructionSequence.compile(<<-CODE
a = 1
-a
CODE
).to_a.join("\n")
You can also use #~$ ruby --dump insns -e 'code'
to print the instruction sequence (as mentioned by @Stefan) which will actually output a much cleaner output.
#~$ ruby --dump insns -e 'a = 1; -a'
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] a
0000 trace 1 ( 1)
0002 putobject 1
0004 setdynamic a, 0
0007 trace 1
0009 getdynamic a, 0
0012 send :-@, 0, nil, 0, <ic:0>
0018 leave
#~$ ruby --dump insns -e 'a = 1; -1 * a'
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] a
0000 trace 1 ( 1)
0002 putobject 1
0004 setdynamic a, 0
0007 trace 1
0009 putobject -1
0011 getdynamic a, 0
0014 opt_mult <ic:1>
0016 leave
you'll notice in the bytecode listing that in the -1 * a
Ruby does an opt_mult
which is basically just an arithmetic operation, and optimized internally.
On the other hand, the -a
bit is an opt_send_without_block
which is an actual method call. My guess is this is why the second version is (a bit) slower.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With