On 10/26/06, Phrogz <> wrote:
> Rick DeNatale wrote:
> > (int & 1).zero? is probably faster than
> > int[0].zero?
> >
> > Since the latter generates the mask using the bit position.
> >
> > and both should be faster than
> >
> > (int % 2).zero?
> >
> > But it it's performance critical, benchmarking is recommended to
> > verify. One should never simply take 'rules of thumb' for granted.
>
> Let's just do it rather than speculating:
>
> require 'benchmark'
>
> ITERATIONS = 1_000_000
> MAX_INT = 2 ** 30
> NUMS = (1..ITERATIONS).map{ rand(MAX_INT) }
> Benchmark.bmbm{ |x|
> x.report '(n % 2).zero?' do
> NUMS.each{ |n|
> (n % 2).zero?
> }
> end
>
> x.report 'n[0].zero?' do
> NUMS.each{ |n|
> n[0].zero?
> }
> end
>
> x.report '(n & 1).zero?' do
> NUMS.each{ |n|
> (n & 1).zero?
> }
> end
> }
>
> #=> Rehearsal -------------------------------------------------
> #=> (n % 2).zero? 2.060000 0.020000 2.080000 ( 2.244595)
> #=> n[0].zero? 1.960000 0.010000 1.970000 ( 2.14523
> #=> (n & 1).zero? 1.990000 0.020000 2.010000 ( 2.275232)
> #=> ---------------------------------------- total: 6.060000sec
> #=>
> #=> user system total real
> #=> (n % 2).zero? 2.070000 0.010000 2.080000 ( 2.315105)
> #=> n[0].zero? 1.960000 0.020000 1.980000 ( 2.14303
> #=> (n & 1).zero? 1.990000 0.010000 2.000000 ( 2.173120)
>
> There is a very, very, very small difference in speed.
Which is why I suggested actually doing the benchmarks after
explaining why mod would be expected to be slower.
Now I took this benchmark and simplified it, taking out the zero? call
in an attempt to isolate the cost of each approach. I also added a
benchmark of an empty loop. On my machine this seems to make the hot
run of mod FASTER than bit testing on 1.8, but not on 1.9:
rick@frodo:/public/rubyscripts$ ruby bitbm.rb
Rehearsal ----------------------------------------------
empty loop 1.840000 0.410000 2.250000 ( 3.366799)
n % 2 3.680000 0.910000 4.590000 ( 5.636832)
n[0] 3.650000 1.020000 4.670000 ( 9.325569)
(n & 1) 3.890000 0.840000 4.730000 ( 6.346543)
------------------------------------ total: 16.240000sec
user system total real
empty loop 1.790000 0.480000 2.270000 ( 3.695585)
n % 2 3.700000 0.880000 4.580000 ( 8.864955)
n[0] 3.740000 0.910000 4.650000 ( 13.832782)
(n & 1) 3.850000 0.870000 4.720000 ( 12.540741)
rick@frodo:/public/rubyscripts$ ruby1.9 bitbm.rb
Rehearsal ----------------------------------------------
empty loop 1.280000 0.010000 1.290000 ( 1.964290)
n % 2 2.900000 0.010000 2.910000 ( 6.053066)
n[0] 2.370000 0.000000 2.370000 ( 3.57292

(n & 1) 2.660000 0.010000 2.670000 ( 4.322753)
------------------------------------- total: 9.240000sec
user system total real
empty loop 1.230000 0.000000 1.230000 ( 2.415479)
n % 2 2.750000 0.010000 2.760000 ( 4.028753)
n[0] 2.390000 0.000000 2.390000 ( 3.322041)
(n & 1) 2.400000 0.010000 2.410000 ( 3.548835)
And n[0] seems faster than n & 1 which is a slight surprise, to me at least.
By the way, is the actual meaning of user, system, total, and real
documented anywhere. I've looked around a few times, and haven't
uncovered it. I guess I could try to devine it by reading the code
for benchmark, but...
Here's my version of Phrogz benchmark code FWIW;
require 'benchmark'
ITERATIONS = 1_000_000
MAX_INT = 2 ** 30
NUMS = (1..ITERATIONS).map{ rand(MAX_INT) }
Benchmark.bmbm{ |x|
x.report 'empty loop' do
NUMS.each {|n|}
end
x.report 'n % 2' do
NUMS.each{ |n|
n % 2
}
end
x.report 'n[0]' do
NUMS.each{ |n|
n[0]
}
end
x.report '(n & 1)' do
NUMS.each{ |n|
(n & 1)
}
end
}
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/