R. *_*des 19 c++ optimization benchmarking visual-c++
Chandler Carruth在他的CppCon2015演讲中引入了两个函数,可以用来对优化器进行一些细粒度的抑制.它们可用于编写微基准测试,优化器不会简单地将其变为无意义.
void clobber() {
asm volatile("" : : : "memory");
}
void escape(void* p) {
asm volatile("" : : "g"(p) : "memory");
}
Run Code Online (Sandbox Code Playgroud)
这些使用内联汇编语句来更改优化程序的假设.
程序集语句声明clobber其中的汇编代码可以在内存中的任何位置读写.实际的汇编代码是空的,但优化器不会查看它,因为它是asm volatile.当我们告诉它代码可能在内存中的任何地方读写时,它都相信它.这有效地防止优化器在调用之前重新排序或丢弃内存写入clobber,并在调用clobber† 后强制执行内存读取.
所述一个中escape,另外使指针p可见的组装块.同样,因为优化器不会查看代码可能为空的实际内联汇编代码,并且优化器仍将假定该块使用指针指向的地址p.这有效地强制p存储器中的任何点而不是寄存器中的点,因为汇编块可能执行从该地址的读取.
(这很重要,因为clobber函数不会强制读取或写入编译器决定放入寄存器的任何内容,因为汇编语句中clobber并未声明组件中的任何内容都必须可见.)
所有这些都发生在没有任何额外代码由这些"障碍"直接生成的情况下.它们纯粹是编译时的工件.
但是,它们使用GCC和Clang支持的语言扩展.有没有办法在使用MSVC时有类似的行为?
†要理解为什么优化器必须这样思考,想象一下汇编块是否为内存中的每个字节添加1的循环.
鉴于你的近似值escape(),你应该对下面的近似值很好clobber()(注意这是一个草案的想法,推迟一些解决方案来实现该函数nextLocationToClobber()):
// always returns false, but in an undeducible way
bool isClobberingEnabled();
// The challenge is to implement this function in a way,
// that will make even the smartest optimizer believe that
// it can deliver a valid pointer pointing anywhere in the heap,
// stack or the static memory.
volatile char* nextLocationToClobber();
const bool clobberingIsEnabled = isClobberingEnabled();
volatile char* clobberingPtr;
inline void clobber() {
if ( clobberingIsEnabled ) {
// This will never be executed, but the compiler
// cannot know about it.
clobberingPtr = nextLocationToClobber();
*clobberingPtr = *clobberingPtr;
}
}
Run Code Online (Sandbox Code Playgroud)
UPDATE
问题:您如何确保"以不可约束的方式" isClobberingEnabled返回false?当然,将定义放在另一个翻译单元中是微不足道的,但是当你启用LTCG时,该策略就会失败.你有什么想法?
答:我们可以利用数论中难以证明的属性,例如费马的最后定理:
bool undeducible_false() {
// It took mathematicians more than 3 centuries to prove Fermat's
// last theorem in its most general form. Hardly that knowledge
// has been put into compilers (or the compiler will try hard
// enough to check all one million possible combinations below).
// Caveat: avoid integer overflow (Fermat's theorem
// doesn't hold for modulo arithmetic)
std::uint32_t a = std::clock() % 100 + 1;
std::uint32_t b = std::rand() % 100 + 1;
std::uint32_t c = reinterpret_cast<std::uintptr_t>(&a) % 100 + 1;
return a*a*a + b*b*b == c*c*c;
}
Run Code Online (Sandbox Code Playgroud)
我用以下内容代替了escape.
#ifdef _MSC_VER
#pragma optimize("", off)
template <typename T>
inline void escape(T* p) {
*reinterpret_cast<char volatile*>(p) =
*reinterpret_cast<char const volatile*>(p); // thanks, @milleniumbug
}
#pragma optimize("", on)
#endif
Run Code Online (Sandbox Code Playgroud)
我认为它并不完美,但已经足够接近了。
可悲的是,我没有办法模仿clobber。
| 归档时间: |
|
| 查看次数: |
712 次 |
| 最近记录: |