如何强制编译器不跳过我的函数调用?

How do I force the compiler not to skip my function calls?

本文关键字:我的 函数调用 何强制 编译器      更新时间:2023-10-16

假设我想对某个函数double a(double b, double c)的两个相互竞争的实现进行基准测试。我已经有一个大的array <double, 1000000> vals,我可以从中获取输入值,所以我的基准测试看起来大致如下:

//start timer here
double r;
for (int i = 0; i < 1000000; i+=2) {
    r = a(vals[i], vals[i+1]);
}
//stop timer here

现在,一个聪明的编译器可以意识到我只能使用最后一次迭代的结果,并简单地杀死其余的,留给我double r = a(vals[999998], vals[999999])。这当然违背了基准测试的目的。

是否有一个好的方法(如果它在多个编译器上工作则加分)来防止这种优化,同时保持所有其他优化?

(我已经看到关于插入空asm块的其他线程,但我担心这可能会阻止内联或重新排序。我也不是特别喜欢在每次迭代期间添加结果sum += r;的想法,因为这是不应该包含在结果计时中的额外工作。为了这个问题的目的,如果我们能专注于其他替代解决方案,那就太好了,尽管对于对此感兴趣的人来说,在评论中有一个热烈的讨论,共识是+=在许多情况下是最合适的方法。)

a放在单独的编译单元中,并且使用LTO(链接时间优化)。这种方式:

  • 循环总是相同的(由于基于a的优化而没有差异)
  • 函数调用的开销总是相同的
  • 要测量纯粹的开销并有一个基线来比较实现,只需对a的空版本进行基准测试

请注意,编译器不能假设对a的调用没有副作用,因此它不能优化循环并将其替换为最后一次调用。


一种完全不同的方法可以使用RDTSC,它是CPU核心中的硬件寄存器,用于测量时钟周期。它有时对微基准测试很有用,但正确理解结果并非微不足道。例如,查看这个并搜索/搜索SO以获取有关rdtsc的更多信息。