测试大整数类加法性能时,如何防止无副作用代码被编译器优化?
要阻止编译器优化掉无副作用的a + b运算,同时避免额外拷贝/存储开销影响计时精度,有以下几种可靠方案:
方法1:利用编译器内联汇编(需适配编译器)
通过插入空汇编指令,让编译器认为运算结果被“使用”,从而保留计算逻辑。
GCC/Clang 示例:
T a, b; // 记录开始时间戳 for (int i = 0; i < 100000; i++) { T t = a + b; // 告知编译器t的地址被汇编指令使用,禁止优化t的生成 __asm__ __volatile__("" : : "r"(&t)); } // 记录结束时间戳并计算平均耗时
__asm__ __volatile__阻止编译器优化掉这段汇编及依赖的计算,"r"(&t)将t的地址作为汇编输入操作数,迫使编译器完成a + b的计算并生成t对象。若T类允许直接访问内部数据(如大整数的存储数组指针),也可传递内部成员地址进一步降低开销。
MSVC 示例:
T a, b; // 记录开始时间戳 for (int i = 0; i < 100000; i++) { T t = a + b; __asm { mov eax, &t; // 仅引用t的地址,无实际操作 } } // 记录结束时间戳并计算平均耗时
方法2:调用“黑盒”函数处理结果
定义一个编译器无法内联、无法分析内部逻辑的函数,将运算结果的地址传入,让编译器认为结果被外部使用:
// GCC/Clang 用__attribute__((noinline)),MSVC 用__declspec(noinline) void __attribute__((noinline)) do_not_optimize(const void*) {} T a, b; // 记录开始时间戳 for (int i = 0; i < 100000; i++) { // 临时对象的地址传入函数,编译器无法优化掉a+b的计算 do_not_optimize(&a + b); } // 记录结束时间戳并计算平均耗时
noinline属性确保函数不会被内联,编译器无法判断函数内部是否操作了传入的指针,因此必须保留a + b的计算过程。这种方法无需依赖汇编,可读性更强。
方法3:用volatile限定临时对象
将运算结果绑定到volatile引用,利用volatile的特性阻止编译器优化:
T a, b; // 记录开始时间戳 for (int i = 0; i < 100000; i++) { // volatile引用延长临时对象生命周期,编译器无法优化其生成 volatile const T& t = a + b; (void)t; // 显式引用确保编译器不会忽略t } // 记录结束时间戳并计算平均耗时
volatile告知编译器变量值可能被外部修改,因此必须完整执行a + b的计算并保留临时对象。配合(void)t的显式引用可进一步确保编译器不会忽略该变量。
内容的提问来源于stack exchange,提问作者lei hu
相关产品推荐
相关产品推荐

