到目前为止,我一直在使用传统的方法来对并发方法进行基准测试,即测量多次运行的持续时间:
template <typename Functor>
double benchmark(Functor const& f, size_t nbRuns)
{
if (nbRuns == 0) { return 0.0; }
f(); // Initialize before measuring, I am not interesting in setup cost
time_t begin = time(0);
for (size_t i = 0; i != nbRuns; ++i) { f(); }
time_t end = time(0);
return difftime(end, begin);
}
在我遇到这个问题之前,这似乎很好,很花哨:Optimizing away a "while(1);" loop in C++0x。
让我感到不同寻常的是,允许编译器在循环之前执行输出......我突然想知道:
什么阻止编译器在循环之前执行
time_t end = time(0);
?
因为如果确实如此,那就会以某种方式搞砸我的小基准代码。
虽然我们在这里,但如果在这种情况下可能会发生重新排序:
如何防止它?
除了C ++以外我无法想到相关标签,如果有人认为我错过了一个,请随意添加
答案 0 :(得分:6)
这是一个棘手的问题。
是什么阻止了编译器 执行time_t end = time(0);之前 循环在这里?
一般来说,没有;事实上,即使在C ++ 03中。由于as-if规则,编译器可以发出任何具有相同可观察行为的代码。这意味着,如果省略f()
不会更改任何指定的输入/输出或挥发性访问,则可能根本不会运行f()
。
令我不同寻常的是 允许编译器执行 在循环之前输出
这不是真的 - 空循环的问题是C ++ 0x不仅仅将非终结视为可观察行为。并不是它可以重新排序空循环和"Hello"
的输出,而是编译器可以完全忽略空循环。
答案 1 :(得分:2)
通常我会使用一个对象将我的计时器放入一个范围内,这样当它超出范围时,它会计算析构函数中的“end”。
是否允许编译器在仍然在范围内时执行其析构函数?我不知道。
当然time_t只测量秒数,所以我通常会测量更细的颗粒,通常是几毫秒。有时毫秒不够精确(例如非常小的函数被称为很多次),在这种情况下你可能会使用微秒。
当然,在这种情况下,进入和离开示波器本身会产生开销,但它通常是“侵入式”分析中的一个很好的衡量标准,通常非常适合在实际情况下进行优化。 (您通常可以打开和关闭此功能)。