C++ 如何强制编译器不跳过函数调用?

C++ 如何强制编译器不跳过函数调用?,c++,benchmarking,compiler-optimization,C++,Benchmarking,Compiler Optimization,假设我想对某个函数的两个相互竞争的实现进行基准测试double a(double b,double c)。我已经有了一个大的数组vals,可以从中获取输入值,因此我的基准测试大致如下所示: //start timer here double r; for (int i = 0; i < 1000000; i+=2) { r = a(vals[i], vals[i+1]); } //stop timer here //在这里启动计时器 双r; 对于(int i=0;i

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

//start timer here
double r;
for (int i = 0; i < 1000000; i+=2) {
    r = a(vals[i], vals[i+1]);
}
//stop timer here
//在这里启动计时器
双r;
对于(int i=0;i<1000000;i+=2){
r=a(VAL[i],VAL[i+1]);
}
//在这里停止计时
现在,一个聪明的编译器可以意识到,我只能使用上一次迭代的结果,而只需杀死其余的部分,留下
double r=a(vals[99998],vals[99999])
。这当然违背了基准测试的目的

有没有一种好方法(如果它在多个编译器上工作,那么就有额外的点数)来防止这种优化,同时保持所有其他的优化


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

a
放在一个单独的编译单元中,不要使用LTO(链接时间优化)。这样:

  • 循环总是相同的(由于基于
    a
    的优化而没有差异)
  • 函数调用的开销总是相同的
  • 要测量纯开销并有一个比较实现的基线,只需对
    a的空版本进行基准测试即可
请注意,编译器不能假设对
a
的调用没有副作用,因此它不能优化循环并用最后一次调用替换它



另一种完全不同的方法可以使用RDTSC,它是CPU核心中测量时钟周期的硬件寄存器。它有时对微基准测试有用,但正确理解结果并不简单。例如,查看并查看/搜索RDTSC的更多信息。

A
放在separa中te编译单元,不使用LTO(链接时间优化)。这样:

  • 循环总是相同的(由于基于
    a
    的优化而没有差异)
  • 函数调用的开销总是相同的
  • 要测量纯开销并有一个比较实现的基线,只需对
    a的空版本进行基准测试即可
请注意,编译器不能假设对
a
的调用没有副作用,因此它不能优化循环并用最后一次调用替换它




另一种完全不同的方法可以使用RDTSC,它是CPU核心中测量时钟周期的硬件寄存器。它有时对微基准测试有用,但正确理解结果并不简单。例如,查看并查看/搜索RDTSC的更多信息。

为什么要这样做,e确切地说?然后对这两个版本都使用
+=
。@us2012根据我目前所看到的情况,我非常怀疑
+=
是否会有“太多的开销”。@us2012:这是解决这个问题的一般方法。有很多专门的方法(将函数放在单独的转换单元中,禁用链接时代码生成器,或将其放在dll中,这样编译器就不知道可以省略函数调用。但一般的方法是确保使用调用的结果。如果您关心性能,那么您必须至少对现代CPU性能。然后你就会知道,
+=
操作的开销基本为零。顺便说一句,我喜欢你的问题说你担心可能会阻止内联的解决方案,然后你接受了保证*内联将被阻止的答案,这涉及到实际禁用某些n编译器优化。显然,忽略了允许编译器尽可能多地内联的解决方案。确切地说,为什么要这样做?然后对这两个版本使用
+=
。@us2012我非常怀疑
+=
是否会“太多开销”根据我目前看到的情况。@us2012:这是解决这个问题的一般方法。有很多专门的方法(将函数放在单独的转换单元中,禁用链接时代码生成器,或将其放在dll中,这样编译器就不知道可以省略函数调用。但一般的方法是确保使用调用的结果。如果您关心性能,那么您必须至少对现代CPU性能。然后你就会知道,
+=
操作的开销基本为零。顺便说一句,我喜欢你的问题说你担心可能会阻止内联的解决方案,然后你接受了保证*内联将被阻止的答案,这涉及到实际禁用某些n编译器优化。显然,忽略了允许编译器尽可能多地内联的解决方案。这将阻止内联,不是吗?是的,因为没有LTO,编译器无法将一个方法从一个TU内联到另一个TU。@us2012:Added RDTSC,也许这是您的一个选项/想法?@us2012知道可能有用RDTSC的可变开销几乎是
+=
@Mysticial:没错,这就是为什么我将另一个链接到qeustion的原因。如果us2012真的陷入了兔子洞,我敢肯定我们会看到更多的qu