Mon05Apr

  1. Divide & Conquer?

    After reading that on embedded systems that when one wishes to divide a number by another, it is more perfomant to multiply by the divisor’s reciprocal instead. If I recall correctly this something to do with some embedded platforms lacking hardware to perform division and so it is emulated in some way, i.e. a lot slower.

    I was wondering what the performance difference between dividing by an integer or multiplying by its reciprocal in Ruby is.

    Benchmark code:

    require 'benchmark'
    
    Benchmark.bmbm do |x|
      x.report("divide:")   { 9999999.times { 999999999 / 8 } }
      x.report("multiply:") { 9999999.times { 999999999 * 0.125 } }
    end
    

    Ruby 1.8.7

    Rehearsal ---------------------------------------------
    divide:     2.160000   0.020000   2.180000 (  2.348516)
    multiply:   2.380000   0.010000   2.390000 (  2.542357)
    ------------------------------------ total: 4.570000sec
    
                    user     system      total        real
    divide:     2.230000   0.010000   2.240000 (  2.406294)
    multiply:   2.420000   0.020000   2.440000 (  2.594445)
    

    Ruby 1.9.1

    Rehearsal ---------------------------------------------
    divide:     1.110000   0.000000   1.110000 (  1.137086)
    multiply:   2.890000   0.020000   2.910000 (  2.953284)
    ------------------------------------ total: 4.020000sec
    
                    user     system      total        real
    divide:     1.110000   0.010000   1.120000 (  1.125234)
    multiply:   2.860000   0.010000   2.870000 (  2.891654)
    

    Ruby 1.9.2 preview release

    Rehearsal ---------------------------------------------
    divide:     1.160000   0.000000   1.160000 (  1.243541)
    multiply:   2.530000   0.020000   2.550000 (  2.723876)
    ------------------------------------ total: 3.710000sec
    
                    user     system      total        real
    divide:     1.160000   0.000000   1.160000 (  1.196763)
    multiply:   2.490000   0.020000   2.510000 (  2.591883)
    

    I tested on a new MacBook Pro 2.53 GHz 4GB. As you can see in 1.8.7 the difference is very small, but on the 1.9.x branch with a different VM, division has been significantly optimised while nominally speaking at least, multiplication performance appears to have slightly deteriorate. Don’t even think about multiplying/dividing a fixnum to/by a float or vice versa if your code may be deployed on JRuby.

    JRuby 1.4

    Rehearsal ---------------------------------------------
    divide:     1.960000   0.000000   1.960000 (  1.783000)
    multiply:  21.506000   0.000000  21.506000 ( 21.505000)
    ----------------------------------- total: 23.466000sec
    
                    user     system      total        real
    divide:     1.962000   0.000000   1.962000 (  2.079000)
    multiply:  23.535000   0.000000  23.535000 ( 23.535000)
    
    

    It turns out JRuby is an absolute dog when using floats and fixnums together in divide/multiply operations. Forget about it too if you’re looking at MacRuby…

    MacRuby 0.5

    Rehearsal ---------------------------------------------
    divide:     0.420000   0.010000   0.430000 (  0.444890)
    multiply:   0.420000   0.000000   0.420000 (  0.434119)
    ------------------------------------ total: 0.850000sec
    
                    user     system      total        real
    divide:     0.420000   0.000000   0.420000 (  0.427087)
    multiply:   0.420000   0.000000   0.420000 (  0.426598)
    

    Since MacRuby is a total BEAST across the board. Insane performance!

    s

    EDIT: These benchmarks were run in IRB. Using JRuby IRB enables ObjectSpace (FWIW Charles Nutter recently made a commit to trunk so that IRB doesn’t require ObjectSpace), which causes an incredible performance hit in JRuby. Running the benchmark as a script yields very impressive performance:

    Rehearsal ---------------------------------------------
    divide:     1.178000   0.000000   1.178000 (  1.097000)
    multiply:   1.078000   0.000000   1.078000 (  1.078000)
    ------------------------------------ total: 2.256000sec
    
                    user     system      total        real
    divide:     1.019000   0.000000   1.019000 (  1.019000)
    multiply:   1.022000   0.000000   1.022000 (  1.022000)