在Chandler Carruth 2015年的CppCon演讲中,他介绍了两个神奇的功能,可以在没有任何额外性能损失的情况下击败优化器。
作为参考,以下是函数(使用GNU风格的内联汇编):
void escape(void* p)
{
asm volatile("" : : "g"(p) : "memory");
}
void clobber()
{
asm volatile("" : : : "memory");
}
它适用于任何支持GNU风格内联汇编的编译器(GCC、Clang、英特尔编译器,可能还有其他编译器)。然而,他提到这在微软风投公司是行不通的。
检查Google Benchmark的实现,他们似乎使用了对volatile const char&
的重新解释转换,并将其传递给非gcc/clang编译器上隐藏在不同翻译单元中的函数。
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
}
// some other translation unit
void UseCharPointer(char const volatile*) {}
然而,我对此有两个担忧:
- 我可能会引发函数调用
- 一个"聪明"的链接时间优化器可能会识别出UseCharPointer很小,将其内联,然后丢弃我想要保留的所有代码,或者一个"巧妙"的优化器可能会被允许执行我不希望它执行的其他重新排序
在MSVC中是否有GNU风格的汇编函数的较低级别等价物?或者这是微软风投上最好的一次?
虽然我不知道MSVC有什么等效的组装技巧,但Facebook在他们的Folly基准库中使用了以下内容:
/**
* Call doNotOptimizeAway(var) against variables that you use for
* benchmarking but otherwise are useless. The compiler tends to do a
* good job at eliminating unused variables, and this function fools
* it into thinking var is in fact needed.
*/
#ifdef _MSC_VER
#pragma optimize("", off)
template <class T>
void doNotOptimizeAway(T&& datum) {
datum = datum;
}
#pragma optimize("", on)
#elif defined(__clang__)
template <class T>
__attribute__((__optnone__)) void doNotOptimizeAway(T&& /* datum */) {}
#else
template <class T>
void doNotOptimizeAway(T&& datum) {
asm volatile("" : "+r" (datum));
}
#endif
这是GitHub上的代码链接。
我一直在寻找一种方法,在我自己的小基准库中实现完全相同的东西。MSVC令人沮丧的是,针对x64不允许__asm技巧,而x86允许!
经过一些尝试,我重新使用了谷歌的解决方案,而没有招致额外的呼叫!好的是,该解决方案可以同时使用MSVC(/Ox)和GCC(-O3)。
template <class T>
inline auto doNotOptimizeAway(T const& datum) {
return reinterpret_cast<char const volatile&>(datum);
}
在调用站点我根本不使用volatile返回!
int main()
{
int a{10};
doNotOptimizeAway(a);
return 0;
}
生成的ASM(编译器资源管理器)
a$ = 8
main PROC
mov DWORD PTR a$[rsp], 10
movzx eax, BYTE PTR a$[rsp]
xor eax, eax
ret 0
main ENDP