加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 避免使用内联asm优化变量

发布时间:2020-12-16 10:06:51 所属栏目:百科 来源:网络整理
导读:我正在阅读 Preventing compiler optimizations while benchmarking,它描述了来自Chandler Carruths talk CppCon 2015: Chandler Carruth “Tuning C++: Benchmarks,and CPUs,and Compilers! Oh My!”的clobber()和escape()如何影响编译器. 通过阅读,我假设
我正在阅读 Preventing compiler optimizations while benchmarking,它描述了来自Chandler Carruths talk CppCon 2015: Chandler Carruth “Tuning C++: Benchmarks,and CPUs,and Compilers! Oh My!”的clobber()和escape()如何影响编译器.

通过阅读,我假设如果我有像“g”(val)这样的输入约束,那么编译器将无法优化掉val.但是在下面的g()中,没有生成代码.为什么?

如何重写notOottimize()以确保为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
}

https://godbolt.org/g/Ndd56K

解决方法

确切地说,为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");
}

对val参数使用g约束,它表示它可以存储在通用寄存器,内存或常量中,无论优化器最方便.由于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.)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读