使用benchmark::DoNotOptimize()时Google Benchmark出现异常结果
调用benchmark::DoNotOptimize()后变量值异常变为0的原因分析
问题现象
使用Google Benchmark进行C++基准测试时,调用benchmark::DoNotOptimize(ret)后,double类型变量ret的值意外从100变为0;但注释该语句或将ret声明为volatile double时,结果符合预期。
原因拆解
1. DoNotOptimize的实现逻辑
你使用的DoNotOptimize版本针对可平凡复制且大小不超过指针大小的类型,实现如下:
template <class Tp> inline BENCHMARK_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copyable<Tp>::value && (sizeof(Tp) <= sizeof(Tp*))>::type DoNotOptimize(Tp& value) { asm volatile("" : "+m,r"(value) : : "memory"); }
其中"+m,r"是GCC的asm约束,允许编译器选择**内存(m)或寄存器(r)**作为操作数存储位置,+表示该操作数为读写型。空汇编语句""本身无实际操作,但volatile和memory clobber会阻止编译器优化这段逻辑。
2. GCC版本的潜在异常
在你使用的GCC 12.2.1版本中,64位系统下sizeof(double) == sizeof(void*),满足模板匹配条件。当编译器选择**寄存器约束(r)**处理double类型时,空asm语句触发了错误的寄存器处理逻辑:导致ret对应的寄存器值被意外清零,同步回内存后变量值变为0。
3. 两种规避方式的原理
- 注释
DoNotOptimize:编译器不会触发上述异常的asm逻辑,变量值自然保持正确; - 声明
volatile double ret:volatile强制变量存储在内存中,此时"+m,r"约束会优先选择内存(m)而非寄存器,避开了寄存器处理的异常,变量值不受影响。
解决方案
- 临时规避:将需要保护的变量声明为
volatile类型;若仅用于调试,可临时注释DoNotOptimize(注意:注释后可能导致编译器优化被测逻辑,正式基准测试需谨慎)。 - 根本修复:
- 升级GCC至13.x及以上版本,该问题大概率已被官方修复;
- 升级Google Benchmark至最新版本,查看是否调整了
DoNotOptimize的约束逻辑(例如强制对浮点数使用内存约束)。
内容的提问来源于stack exchange,提问作者Mingfei Gao
相关产品推荐
相关产品推荐

