0

所以今天早上我决定第一次玩基准测试。

我很好奇具有“do-end”块格式与“{}”格式的代码之间的速度差异。

所以我将 Benchmark 代码存储在 Proc 中,这样我就可以连续多次调用它:

n = 100_000_000
bmp = Proc.new do
  Benchmark.bm do |x|
    x.report {n.times {a = "1"}}
    x.report {n.times do; a = "1"; end}
  end
end

当我运行一次时,我们的结果是预期的。

>> bmp.call
  user     system      total        real
1.840000   0.030000   1.870000 (  1.874507)
1.860000   0.050000   1.910000 (  1.926101)
=> true

但后来又跑了。

>> bmp.call
  user     system      total        real
1.870000   0.050000   1.920000 (  1.922810)
1.840000   0.000000   1.840000 (  1.850615)

对我来说,这看起来与我的预期完全相反。我熟悉分支预测的概念。这是分支预测的经典例子吗?如果不是,那是什么?有没有办法防止这样的不准确(如果这甚至被认为是一个)?

编辑:经过一些建议,我确实运行了这段代码超过 30 次。通常它会在两个结果之间交替。数据样本可在此处找到:

gist.github.com/TheLarkInn/5599676

4

2 回答 2

4

首先,您的基准完全没有意义。do/end语法和{/语法的区别}就在于:语法。没有语义差异。因此,两者之间不可能有任何运行时性能差异。这在逻辑上是不可能的。您无需对其进行基准测试。

唯一可能存在的性能差异是一个比另一个需要更长的时间来解析。但是,两者中没有一个比另一个更难解析。唯一的区别是优先级。因此,解析中也很可能没有任何性能差异。

即使在解析存在性能差异,您的基准测试也不会显示出来。您正在使用用 Ruby 编写的基准测试,但是为了运行 Ruby 代码,Ruby 执行引擎必须首先解析它,这意味着在您的基准测试开始之前解析就已经发生了。因此,即使您的基准测试不是毫无意义的,它仍然是无用的,因为它不可能衡量解析中的性能差异。

至于你关于分支预测的问题:你的代码中没有分支,没有什么可预测的。

顺便说一句:即使你的基准测试是为了不同的目的,它仍然不会测量任何东西,因为至少更高级的 Ruby 实现会认识到你的块基本上是无操作的,并简单地将它们优化掉。即使它们没有被优化掉,它们所测量的只是内存分配器的性能(分配几百兆字节的小String对象),而不是块的性能。

于 2013-05-17T14:36:17.363 回答
1

只是统计数据的快速入门:

我不确定两次运行是否足以发现趋势。如果第二次运行时两个测试块之间的系统负载存在差异怎么办?

确定两个样本之间的统计差异的经验法则是 30 个或更多数据点将为您提供统计相关的结果。

我会至少多次运行您的测试,独立存储两个版本的结果,然后在内部比较它们以确保它们是一致的,然后再将两组进行比较。

可能是您最初的前提不正确:)

于 2013-05-17T13:45:47.370 回答