Bee*_*ope 10 c optimization x86 built-in icc
开发人员可以使用__builtin_expect 内置函数来帮助编译器了解分支可能走向哪个方向.
将来,我们可能会为此目的获得一个标准属性,但截至今天至少全部clang,icc并gcc支持非标准属性__builtin_expect.
但是,icc当你使用它时,似乎会生成奇怪的代码1.也就是说,无论使用哪个方向进行预测,使用内置函数的代码都严格地比没有内置代码的代码更糟糕.
以下面的玩具功能为例:
int foo(int a, int b)
{
do {
a *= 77;
} while (b-- > 0);
return a * 77;
}
Run Code Online (Sandbox Code Playgroud)
在三个编译器中,icc唯一一个将其编译为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, edi, 77 #6.14
ret
Run Code Online (Sandbox Code Playgroud)
无论GCC和Clang的管理无缘简单的解决方案,并使用5条指令.
另一方面,当你在循环条件下使用likely或unlikely宏时,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;
}
Run Code Online (Sandbox Code Playgroud)
这个循环在功能上等同于前一个循环(因为__builtin_expect只返回它的第一个参数),但icc会产生一些可怕的代码:
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, edi, 77 #8.6
test edx, edx #9.12
jne ..B1.2 # Prob 95% #9.12
imul eax, edi, 77 #11.15
ret #11.15
Run Code Online (Sandbox Code Playgroud)
该函数的大小增加了一倍,达到10条指令,并且(更糟糕的是!)关键循环的数量增加了一倍多,达到7条指令,其中一条长关键依赖链涉及一个cmov奇怪的东西.
如果您使用unlikely提示,并且也使用Godbolt支持的所有icc版本(13,14,17),情况也是如此.因此,无论提示如何,代码生成都严格地更糟,并且无论实际的运行时行为如何.
使用提示时,既gcc不会clang降低也不会有任何降级.
那是怎么回事?
1至少在我试过的第一个和后续的例子中.
对我来说这似乎是一个 ICC 错误。此代码(可在 godbolt 上找到)
int c;
do
{
a *= 77;
c = b--;
}
while (likely(c > 0));
Run Code Online (Sandbox Code Playgroud)
仅使用辅助局部 var ,生成不带模式的c输出edx = !!(esi > 0)
foo(int, int):
..B1.2:
mov eax, esi
dec esi
imul edi, edi, 77
test eax, eax
jg ..B1.2
Run Code Online (Sandbox Code Playgroud)
不过,仍然不是最佳的(没有它也可以eax)。
我不知道ICC官方的政策__builtin_expect是完全支持还是只是兼容性支持。
这个问题似乎更适合ICC官方论坛。
我已经尝试在那里发布这个主题,但我不确定我是否做得很好(我已经被这样宠坏了)。
如果他们回答我,我会更新这个答案。
编辑
我在英特尔论坛上得到了答案,他们在跟踪系统中记录了这个问题。
就像今天一样,这似乎是一个错误。
| 归档时间: |
|
| 查看次数: |
338 次 |
| 最近记录: |