Ruby每个vs while循环的性能

Ruby每个vs while循环的性能,ruby,performance,while-loop,each,Ruby,Performance,While Loop,Each,尝试用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)

尝试用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
def余数(数字)#带while
除数=2
当除数小于21时
返回false,除非数字%divisior==0
除数+=1
结束
真的
结束
def余数(数字)#每个
(2..20).每个do除数|
返回false,除非数字%divisior==0
结束
真的
结束
数量=180_000_000
而数量<10_000_000_000
if余数
放入“#{number}”
打破
结束
数字+=1
结束

在我的电脑上,使用while版本,Ruby大约需要10秒,每个版本需要70到80秒来解析。代码做完全相同的事情,给出相同的结果。为什么在性能上会有这样的差异?

每个
都是一种方法,在C中使用
for
循环来实现它,这(在C中)与
while
循环做的事情相同。在内部,它需要保留一个计数器,将其初始化为
2
,并将其递增至
20
——这与
版本需要执行的操作相同。但是
each
版本也有创建函数(块)的开销,将其发送到
each
方法,并在
each
方法实现中的
for
循环的每次迭代中调用它。所有这些都需要计算机做额外的工作,这使得代码速度变慢。

似乎成本增加了:

  • 为范围对象(2..20)创建枚举器
  • 调用
    每个
  • 这是一个基准

    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
    
    上述示例输出为:

                  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]
    
    可以看出,范围对象的创建不是问题,但为其创建枚举器会增加时间,将块传递给该迭代器并执行一些代码会增加进一步的成本

    与此相比,
    while
    循环实现正在执行基本操作。因此,速度更快


    请注意,
    for
    循环的性能将与
    每个
    一样糟糕,因为它或多或少等同于
    每个
    实现

    ,并且
    每个
    版本创建并垃圾收集一个
    (2..20)
    range
    number
    times…#一个range上的每一个都是用
    for
    loop-@NeilSlater实现的-我想的是同样的事情,但不是
    while;在做某事时结束
    ;结束
    等价物?@Anthony:不是所有出现在Ruby代码中的
    都能创建块。
    do
    版本虽然
    没有创建块AFAIK,但它只是一种语法变体。@Anthony:是的,你是对的-一个范围的
    每个
    都是使用
    for
    循环实现的。我会修好的。然而,它是在C中使用C的
    for
    循环来实现的,这是一个在一段时间内的语法糖循环,而不是使用Ruby的
    for
    循环,后者实际上是使用
    每个
    方法来实现的。同样值得注意的是,您将这些小的ish成本与
    返回false的成本进行比较,除非number%除数==0
    ,这根本不是什么Ruby代码。在Ruby中,您应该避免在范围上实例化枚举数,或者使用基于块的方法来构造代码,这样做是错误的。只有在极少数情况下,你才会看到巨大的时差