微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么icc无法以合理的方式处理编译时分支提示?

开发人员可以使用__builtin_expect builtin来帮助编译器 understand分支可能走向哪个方向.

将来,为了这个目的,我们可能会得到一个standard attribute,但是截至今天,至少所有的clang,icc和gcc都支持非标准__builtin_expect.

但是,当您使用它时,icc似乎会产生奇怪的代码.也就是说,使用内置代码代码严格比没有它的代码更差,无论预测的方向如何.

以下列玩具功能为例:

int foo(int a,int b)
{
  do {
     a *= 77;
  } while (b-- > 0);  
  return a * 77;
}

在三个编译器中,icc是唯一一个编译为optimal scalar loop的3个指令的编译器:

foo(int,int):
..B1.2:                         # Preds ..B1.2 ..B1.1
        imul      edi,edi,77                                  #4.6
        dec       esi                                           #5.12
        jns       ..B1.2        # Prob 82%                      #5.18
        imul      eax,77                                  #6.14
        ret

gccClang都管理了轻松的解决方案,并使用了5条指令.

另一方面,当您使用可能或不太可能的宏在循环条件下,icc完全是脑力衰竭:

#define likely(x)   __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)

int foo(int a,int b)
{

   do {
     a *= 77;
  } while (likely(b-- > 0));  

   return a * 77;
}

这个循环在功能上等同于前一个循环(因为__builtin_expect只是返回其第一个参数),而icc produces some awful code

foo(int,int):
        mov       eax,1                                        #9.12
..B1.2:                         # Preds ..B1.2 ..B1.1
        xor       edx,edx                                      #9.12
        test      esi,esi                                      #9.12
        cmovg     edx,eax                                      #9.12
        dec       esi                                           #9.12
        imul      edi,77                                  #8.6
        test      edx,edx                                      #9.12
        jne       ..B1.2        # Prob 95%                      #9.12
        imul      eax,77                                  #11.15
        ret                                                     #11.15

功能的大小增加到10个指令,而且(更糟糕的是),关键循环已经超过了一倍,达到了7个指令,长期的关键依赖链涉及到一个cmov和其他奇怪的东西.

如果您使用unlikely hint以及godbolt支持的所有icc版本(13,14,17),也是如此.所以代码生成是严格的,不管提示,不管实际的运行时行为.

当使用提示时,gcc和cl Ne都不会遭受任何退化.

怎么了?

至少在我尝试的第一个和后来的例子中.

解决方法

对我来说似乎是一个ICC错误.这段代码( available on godbolt)
int c;

do 
{
    a *= 77;
    c = b--;
} 
while (likely(c > 0));

只需使用辅助局部变量c,就可以产生没有edx = !!(esi> 0)模式的输出

foo(int,int):
  ..B1.2:                         
    mov       eax,esi
    dec       esi
    imul      edi,77
    test      eax,eax
    jg        ..B1.2

仍然不是最佳的(它可以没有eax),但是.

我不知道官方ICC policy about __builtin_expect is full support or just compatibility support.

这个问题似乎更适合Official ICC forum.
我试过posting this topic there,但我不知道我做得很好(我已经被SO弄坏了).
如果他们回答我,我会更新这个答案.

编辑我在英特尔论坛上得到了一个答案,他们在跟踪系统中记录了这个问题.像今天一样,这似乎是一个错误.

原文地址:https://www.jb51.cc/c/114308.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐