a...">

Why is `-1 * x` faster than` -x` and why?

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 will return this result 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) 

It looks intuitive, because if I think I would set it to "-x" rather than "-1 * x" to be faster.

Why is the difference? Or do I have some major flaw in my measurements?

+8
ruby micro-optimization
source share
1 answer

This can 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 a sequence of commands (as @Stefan pointed out ), which actually produces a much cleaner result.

 #~$ 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 will notice in the bytecode list that at -1 * a Ruby does opt_mult , which is basically just an arithmetic operation and optimized internally. On the other hand, the -a bit is opt_send_without_block , which is the actual method call. My guess is why the second version is (slightly) slower.

+12
source share

All Articles