我试图确切地了解谷歌DoNotOptimize()
应该如何工作。
为了完整起见,这里是它的定义(对于clang和非常量数据):
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
asm volatile("" : "+r,m"(value) : : "memory");
}
据我了解,我们可以在这样的代码中使用它:
start_time = time();
bench_output = run_bench(bench_inputs);
result = time() - start_time;
为了确保基准保持在关键部分:
start_time = time();
DoNotOptimize(bench_inputs);
bench_output = run_bench(bench_inputs);
DoNotOptimise(bench_output);
result = time() - start_time;
具体来说,我不明白的是为什么这保证(是吗?)run_bench()
没有移到上面start_time = time()
。
(有人在此评论中确切地问了这个问题,但是我不明白答案)。
据我了解,上面DoNotOptimze()
做了几件事:
- 它强制
value
堆栈,因为它是通过 C++ 引用传递的。你不能有一个指向寄存器的指针,所以它必须在内存中。 - 因为
value
现在在堆栈上,所以随后破坏内存(如在 asm 约束中所做的那样)将强制编译器假定value
调用DoNotOptimize(value)
. - (我不清楚
+r,m
约束是否相关。据我所知,这表明指针本身可能存储在寄存器或内存中,但指针值本身可能被读取和/或写入。)
这就是我变得模糊的地方。
如果start_time
还分配了堆栈,则内存破坏DoNotOptimize()
将意味着编译器必须假定DoNotOptimize()
可能读取start_time
. 因此语句的顺序只能是:
start_time = time(); // on the stack
DoNotOptimize(bench_inputs); // reads start_time, writes bench_inputs
bench_output = run_bench(bench_inputs)
但是如果start_time
不存储在内存中,而是存储在寄存器中,那么破坏内存不会破坏start_time
,对吧?在这种情况下,所需的start_time = time()
and顺序DoNotOptimize(bench_inputs)
会丢失,编译器可以自由地执行以下操作:
DoNotOptimize(bench_inputs); // only writes bench_inputs
bench_output = run_bench(bench_inputs)
start_time = time(); // in a register
显然我误解了一些东西。谁能帮忙解释一下?谢谢 :)
我想知道这是否是因为重新排序优化发生在寄存器分配之前,因此所有东西都被认为是当时堆栈分配的。但如果是这样的话,那DoNotOptimize()
将是多余的,ClobberMemory()
就足够了。