我void DoNotOptimize
对 Google Benchmark Framework 的功能实现有点困惑(定义来自这里):
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
#if defined(__clang__)
asm volatile("" : "+r,m"(value) : : "memory");
#else
asm volatile("" : "+m,r"(value) : : "memory");
#endif
}
所以它实现了变量,如果是非常量,还告诉编译器忘记它之前的值。("+r"
是一个 RMW 操作数)。
并且总是使用一个clobber,它是一个防止重新排序加载/存储的编译器"memory"
屏障,即确保所有全局可访问对象的内存与C++抽象机同步,并假设它们也可能已被修改。
我远不是低级代码专家,但据我了解实现,该函数用作读/写障碍。所以 - 基本上 - 它确保传入的值要么在寄存器中,要么在内存中。
虽然如果我想保留函数的结果(应该进行基准测试),这似乎是完全合理的,但我对留给编译器的自由度感到有点惊讶。
我对给定代码的理解是,编译器可能会在每次调用时插入一个具体化点DoNotOptimize
,这意味着在重复执行时(例如,在循环中)会产生大量开销。当不应该优化的值只是单个标量值时,如果编译器确保该值驻留在寄存器中似乎就足够了。
区分指针和非指针不是一个好主意,例如:
template< class T >
inline __attribute__((always_inline))
void do_not_optimize( T&& value ) noexcept {
if constexpr( std::is_pointer_v< T > ) {
asm volatile("":"+m"(value)::"memory");
} else {
asm volatile("":"+r"(value)::);
}
}