Reputation: 548
trying to resolve a basic problem of algorithm in Ruby, and testing performance.
Just in case, the algorithm aims to find the smallest positive number that is evenly divisible by all of the numbers from 1 to 20. Here's the code :
def remainder(number) # with while
divisor = 2
while divisor < 21
return false unless number % divisor == 0
divisor += 1
end
true
end
def remainder(number) # with each
(2..20).each do |divisor|
return false unless number % divisor == 0
end
true
end
number = 180_000_000
while number < 10_000_000_000
if remainder number
puts "#{number}"
break
end
number += 1
end
On my computer, with while version, Ruby takes around 10 seconds, with each version, it takes between 70 and 80 seconds to resolve. Code does the exact same thing, gives same result. Why such a difference in performance ?
Upvotes: 12
Views: 6206
Reputation: 12603
each
is a method, and it's implemented using in C using a while
for
loop, which (in C) does the same thing you while
loop does. Internally it needs to hold a counter, initialize it to 2
and increment it up to 20
- the same thing your while
version needs to do. But the each
version also has the overhead of creating a function(the block), sending it to the each
method, and calling it on every iteration of the for
loop inside the each
method's implementation. All this requires extra work from the computer which makes the code slower.
Upvotes: 3
Reputation: 18762
It seems the cost is added by:
each
Here is a benchmark
require 'benchmark'
c = 100_000
Benchmark.bm(7) do |x|
x.report("range - 1 :") { c.times { (2..20) } }
x.report("range - 2 :") { c.times { (2..20).each } }
x.report("range - 3 :") { c.times { (2..20).each { |x| x } } }
end
Sample output of above is:
user system total real
range - 1 : 0.000000 0.000000 0.000000 ( 0.006004)
range - 2 : 0.031000 0.000000 0.031000 ( 0.026017)
range - 3 : 0.125000 0.000000 0.125000 ( 0.122081)
[Finished in 0.4s]
As can be seen, creation of Range object is not a problem, but creating an enumerator for it adds time, and passing a block to that iterator and executing some code, adds further cost.
Compared to this, the while
loop implementation is doing primitive operations. Hence, is faster.
Please note that for
loop will perform as badly as each
, as it is more or less equivalent to each
implementation
Upvotes: 6