Ruby基准测试准确性-分支预测处于最佳状态?

Ruby基准测试准确性-分支预测处于最佳状态?,ruby,processor,branch-prediction,Ruby,Processor,Branch Prediction,所以今天早上我决定第一次玩基准测试 我对使用“do end”块格式与使用“{}”格式的代码之间的速度差异感到好奇 因此,我将基准代码存储在一个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 当我运行一次时,我的结果是预期的 >

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

我对使用“do end”块格式与使用“{}”格式的代码之间的速度差异感到好奇

因此,我将基准代码存储在一个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

只是一个关于统计数据的快速入门:

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

确定两个样本之间统计差异的经验法则是,30个或更多数据点将给出统计相关的结果

我至少会运行您的测试很多次,分别存储两个版本的结果,然后在内部比较以确保它们一致,然后再将两个集相互比较


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

只是简单介绍一下统计数据:

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

确定两个样本之间统计差异的经验法则是,30个或更多数据点将给出统计相关的结果

我至少会运行您的测试很多次,分别存储两个版本的结果,然后在内部比较以确保它们一致,然后再将两个集相互比较


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

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

唯一可能存在的性能差异是,一个需要比另一个更长的时间来解析。然而,这两种方法都不比另一种更难解析。唯一的区别是优先级。因此,解析过程中很可能也没有任何性能差异

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

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


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

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

唯一可能存在的性能差异是,一个需要比另一个更长的时间来解析。然而,这两种方法都不比另一种更难解析。唯一的区别是优先级。因此,解析过程中很可能也没有任何性能差异

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

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


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

系统负载不应该是主要因素,我认为
基准测试
是收集进程时钟(所以只注册代码正在做的事情,并且非常擅长忽略其他进程,尽管不是所有的事情——争用仍然可能发生在磁盘、RAM等上)。然而,这意味着尽管重复了1亿次,但在这个示例中只有约190个布尔值(“CPU正在运行我的进程”)