Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Ruby's loop command slower than while true?

Ruby has a built-in loop command that executes the block following it forever (or until stopped by break). However, when comparing it against the functionally similar while true, it is significantly slower:

require "benchmark/ips"

NUMBER = 100_000_000

def fast
  index = 0
  while true
    break if index > NUMBER
    index += 1
  end
end

def slow
  index = 0
  loop do
    break if index > NUMBER
    index += 1
  end
end

Benchmark.ips do |x|
  x.report("While Loop")  { fast }
  x.report("Kernel loop") { slow }
  x.compare!
end

Under Ruby 2.4.1 (p111 (2017-03-22 revision 58053) [x64-mingw32]), the difference is striking:

Warming up --------------------------------------
          While Loop     1.000  i/100ms
         Kernel loop     1.000  i/100ms
Calculating -------------------------------------
          While Loop      0.630  (± 0.0%) i/s -      4.000  in   6.350897s
         Kernel loop      0.190  (± 0.0%) i/s -      1.000  in   5.274249s

Comparison:
          While Loop:        0.6 i/s
         Kernel loop:        0.2 i/s - 3.32x  slower

Why is there such a performance difference? And why is the single-purpose loop command worse at its job than the general-purpose while?

(Benchmark copied from here, licensed under CC-BY-SA)

like image 454
Mangara Avatar asked Jul 13 '17 00:07

Mangara


1 Answers

loop is a kernel method which takes a block. As a reminder, a block introduces new local variable scope.

For example:

loop do
 a = 2
 break
end
puts a

Will return an error such as: "NameError: undefined local variable or method `a' for main:Object" On the other hand:

while true
 a = 2
 break
end
p a #=> return a = 2

So I wouldn't be surprised that loop creates some sort of local variable(s) such as one for the break statement that is (are) going to be in its scope. Creating/deleting those variables at every iteration slow down the process.

like image 129
Stephane Paquet Avatar answered Nov 10 '22 01:11

Stephane Paquet