我正在阅读Preventing compiler optimizations while benchmarking,其中介绍了Chandler Carruths谈话CppCon 2015: Chandler Carruth "Tuning C++: Benchmarks, and CPUs, and Compilers! Oh My!"中的clobber()
和escape()
如何影响编译器。
通过阅读,我假设如果我有一个像“g”(val
)这样的输入约束,那么编译器将无法优化val
。但是在下面的g()
中,不会生成任何代码。为什么呢?
如何重写doNotOptimize()
以确保为g()
生成代码?
template <typename T>
void doNotOptimize(T const& val) {
asm volatile("" : : "g"(val) : "memory");
}
void f() {
char x = 1;
doNotOptimize(&x); // x is NOT optimized away
}
void g() {
char x = 1;
doNotOptimize(x); // x is optimized away
}
答案 0 :(得分:6)
为g()生成代码究竟是什么意思?如果你自己编写,你会写什么代码?说真的,这是一个真实的问题。在开始从编译器中调用它之前,你必须决定你期望的输出。
无论如何,让我们来看看你现在拥有的东西。在f(),
void f() {
char x = 1;
doNotOptimize(&x); // x is NOT optimized away
}
您正在使用x
的地址,这会阻止优化器将其分配到寄存器中。它必须在内存中分配才能拥有一个地址。
但是,在g()中,
void g() {
char x = 1;
doNotOptimize(x); // x is optimized away
}
x
只是一个局部变量,任何理智的优化器都会在寄存器中分配它,或者在这种情况下作为常量。这是允许的,因为你从不接受它的地址;你只是用它的价值。因此,例如,编译器可能会生成如下代码:
g():
mov al, 1 // store 1 in BYTE-sized register AL
...
或者在这种情况下根本不生成任何代码,并用它的常量值代替变量的任何使用。
您的doNotOptimize
代码,
template <typename T>
void doNotOptimize(T const& val) {
asm volatile("" : : "g"(val) : "memory");
}
对g
参数使用val
约束,该参数表示它可以存储在 通用寄存器,内存或作为常量,无论优化器哪个最方便。由于val
是常量,因此在内联此调用时,优化程序将其保留为常量。你的“内存”clobber说明符没有效果,因为这里没有修改内存。
那我们该怎么办?好吧,我们可以强制变量x
在内存中分配,即使它不需要,也可以使用m
约束:
template <typename T>
void doNotOptimize(T const& val) {
asm volatile("" : : "m"(val) : "memory");
}
void g() {
char x = 1;
doNotOptimize(x);
}
现在编译器无法优化x
的存储,并强制发出以下代码:
g():
mov BYTE PTR [rsp-1], 1
ret
请注意,这与声明x
变量volatile
所具有的效果基本相同。
还记得我刚开始提出的问题吗?那是你想要的输出吗?
或许,您可能希望编译器发出即时寄存器移动。如果是这样,r
约束将起作用 - 或any of the x86-specific constraints允许您指定特定寄存器。这会强制优化器在寄存器中分配值,即使它不需要:
g():
mov eax, 1
ret
但是,我不能看出其中任何一个的重点是什么。
如果你想制作一个微基准测试来测试使用单个const-reference参数调用函数的开销,那么更好的选择是确保调用的函数的定义对于优化器是不可见的。然后,它无法内联该功能,并且具有来安排进行调用,包括所有必要的设置。如果您只是studying how a compiler might emit that code,这也很有效。 (当然,你不能使用模板功能。好吧,除非你想要abuse C++11's extern
templates。)
答案 1 :(得分:1)
我建议宣布
volatile char x = 1;
但请注意编译器是&#34;对&#34;像观察一样优化。
答案 2 :(得分:0)
没有为g()
生成代码,因为"g"
约束允许将输入优化为常量。