Ruby每个vs while循环性能

ghi*_*ert 11 ruby each performance while-loop

试图解决Ruby中算法的基本问题,并测试性能.

以防万一,算法的目的是找到最小的正数,它可以被1到20之间的所有数字整除.这是代码:

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
Run Code Online (Sandbox Code Playgroud)

在我的计算机上,使用while版本,Ruby大约需要10秒,每个版本需要70到80秒才能解决.代码完全相同,给出相同的结果.为什么性能如此差异?

Wan*_*ker 5

似乎成本增加了:

  1. 创建范围对象的枚举器(2..20)
  2. 调用块中的 each

这是一个基准

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
Run Code Online (Sandbox Code Playgroud)

上面的示例输出是:

              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]
Run Code Online (Sandbox Code Playgroud)

可以看出,创建Range对象不是问题,但为它创建枚举器会增加时间,并将块传递给该迭代器并执行一些代码会增加成本.

与此相比,while循环实现正在进行基本操作.因此,更快.

请注意,for循环将表现得很糟糕each,因为它或多或少等同于each实现

  • 另外值得注意的是,您正在比较这些小成本相对于"返回false"的成本,除非数字%divisor == 0`,这根本不是Ruby代码.在Ruby中你应该避免在Ranges上实例化枚举器或使用基于块的方法来构造代码,这是错误的.只有在极少数情况下,您才会看到很大的时差 (3认同)