为什么Ruby的循环命令比while true慢?

9

Ruby内置了一个loop命令,它执行紧随其后的代码块,一直循环执行(或直到被break停止)。然而,与功能类似的while true相比,它明显更慢:

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

在 Ruby 2.4.1 (p111 (2017-03-22 revision 58053) [x64-mingw32]) 中,差异非常明显:
Warming up --------------------------------------
          While Loop     1.000  i/100ms
         Kernel loop     1.000  i/100ms
Calculating -------------------------------------
          While Loop      0.6300.0%) i/s -      4.000  in   6.350897s
         Kernel loop      0.1900.0%) i/s -      1.000  in   5.274249s

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

为什么性能会有这样的差异?为什么单一目的的 loop 命令比通用的 while 在其工作方面更差呢?
(基准测试参考自 这里,根据 CC-BY-SA 许可证授权)

1
使用 begin i+=1 end while i<=NUMBER 仍然更快。想象一下... - dawg
2
Kernel#loop 会捕获 StopIteration 异常。它的响应是跳出循环。如果 enum = [1,2,3].to_enum; loop do; enum.next; end,当枚举器生成其最后一个值后执行 next 时,将引发 StopIteration 异常。理论上,相关开销可能会影响基准测试结果,但我预计使用 while(true)while(1)(FORTRAN 遗留物)节省的时间通常会被执行循环内语句所花费的时间所抵消。为了保持一致性,我总是使用 loop - Cary Swoveland
2个回答

10

loop 是一个接受 block 的内核方法。提醒一下,block 会引入新的本地变量范围

例如:

loop do
 a = 2
 break
end
puts a

会返回类似这样的错误: "NameError: 未定义局部变量或方法 `a' for main:Object" 另一方面:

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

所以我不会感到惊讶,如果loop创建一些局部变量,比如用于break语句的变量将在其作用域内。在每次迭代时创建/删除这些变量会减慢进程。


关于循环的快速备注:
  1. 将条件放在结尾通常更好。
  2. 扩展条件似乎可以提高性能(两个循环),因此编写以下代码:f index > NUMBER break end 您可以在此处获取更多信息 https://launchschool.com/books/ruby/read/loops_iterators
- Stephane Paquet

-5

通常为了获得更准确的基准测试结果,您可以增加测试次数并将结果平均分配到基准测试数量上。

while循环在每次循环的顶部都有一个条件检查,而相比之下,loop do...end没有条件检查。因此,即使该条件为真,它也会计算较少的逻辑,但仍然至少多做一次检查。


7
这似乎并不能回答问题。尽管条件语句中的代码块未必会被执行,但while循环被提出作为更快的选项。 - Brad Werth
最好的寻找答案的地方应该是源代码。因此,可以比较运行的内容,而不仅仅是基准测试。 - broken_historian
5
也许下次在发表错误的猜测回答之前,你应该这样做… - Brad Werth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接