【问题标题】:A loop with many iterations makes execution incredibly slow具有多次迭代的循环使执行速度非常慢
【发布时间】:2016-05-31 09:10:25
【问题描述】:

此代码的执行时间约为 1 秒:

start_time = Time.now

prev = 1
(1..1000).each do |i|
  (1..10000).each do |j|
    result = j * prev
    result = result + prev
    result = result - prev
    result = result / prev
    prev = j
  end
end

end_time = Time.now
printf('%f sec', end_time - start_time)

但是当我使用一个循环进行 10000000 次迭代(而不是上面写的 2 个循环、1000 和 10000 次迭代)时,它变得慢得多(大约 4.5 秒):

start_time = Time.now

prev = 1
(1..10000000).each do |j|
  result = j * prev
  result = result + prev
  result = result - prev
  result = result / prev
  prev = j
end

end_time = Time.now
printf('%f sec', end_time - start_time)

为什么会这样?总迭代次数还是一样的。

【问题讨论】:

  • 尝试在每种情况下调查result 的值。我认为10_000_000 * 9_999_999 大于10_000 * 9_999
  • 第一个代码有 1.35 秒,第二个代码有 1.24 秒。变量result 不会过度增长,因为它每次都会重置。
  • 所以基本上,第一个代码的迭代是 1.35 秒,第二个是 1.24 秒?奇怪的。尝试以较小的块记录执行,例如每 20% 的块。通过简单的检查,总负载应该会增加,因此不会明显影响测试。
  • 无法重现:第一个是1.288465 sec,第二个是1.219005 sec在我的电脑上。你想通过这些例子达到什么目的?
  • @spickermann 我只是在控制台中运行这些脚本 :) Ruby 2.3 x64,Windows 10 x64。也许是操作系统导致了问题?

标签: ruby loops execution-time


【解决方案1】:

第二个示例处理的数字比第一个大得多(正如上面@Sergii K 评论的那样)。第二个示例代码可能会达到系统上的最大 Fixnum 限制。在 32 位系统上,maximum signed integer2**(32-1) - 1 = 2147483647,它远小于第二个示例中的最大乘积 j * prev(与第一个示例中的最大乘积相反)。在这种情况下,ruby 必须在内部将Fixnums 转换为Bignums,这就是为什么第二个示例代码可能比第一个慢。

在 64 位系统上,我希望两个样本几乎同时运行,因为最大的整数永远不会达到 Fixnum 限制。这就是为什么大多数其他评论者没有看到时间上有很大差异的原因。

更新:如果最大 Fixnum 数仅为 1073741823,正如上面 OP 所评论的那样,那么这一定意味着虽然操作系统本身是 64 位,也许安装的 ruby​​ 也是 64 -bit ruby​​,它仍然只使用 4 个字节来存储 Fixnum 数字(而不是 8 在真正的 64 位 ruby​​ 中)。最大整数值远小于第二个示例中所需的值,因此它确实必须将较高的数字转换为 Bignums,这就是第二个示例的缓慢的原因。

如果您比较,您可以自己检查:

(2**(0.size * 8 -2) -1).class      # => Fixnum vs:
(2**(0.size * 8 -2) -1 + 1).class  # => should be Bignum

【讨论】:

    猜你喜欢
    • 2015-03-24
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-07
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多