Joh*_*nck 19 c c++ optimization gcc compiler-optimization
这是我的代码:
int f(double x, double y)
{
return std::isnan(x) || std::isnan(y);
}
Run Code Online (Sandbox Code Playgroud)
如果您使用的是C而不是C++,只需替换std::为__builtin_(不要简单地删除std::,原因如下所示:为什么GCC对C++ <cmath>比C <math.h>更有效地实现isnan()?).
这是集会:
ucomisd %xmm0, %xmm0 ; set parity flag if x is NAN
setp %dl ; copy parity flag to %edx
ucomisd %xmm1, %xmm1 ; set parity flag if y is NAN
setp %al ; copy parity flag to %eax
orl %edx, %eax ; OR one byte of each result into a full-width register
Run Code Online (Sandbox Code Playgroud)
现在让我们尝试另一种做同样事情的配方:
int f(double x, double y)
{
return std::isunordered(x, y);
}
Run Code Online (Sandbox Code Playgroud)
这是替代方案的组装:
xorl %eax, %eax
ucomisd %xmm1, %xmm0
setp %al
Run Code Online (Sandbox Code Playgroud)
这很棒 - 我们将生成的代码减少了一半!这工作,因为ucomisd套,如果奇偶标志或者其操作数为NAN,所以我们可以在同一时间,SIMD式测试两个值.
您可以在野外查看原始版本的代码,例如:https://svn.r-project.org/R/trunk/src/nmath/qnorm.c
如果我们能够让GCC足够聪明,可以isnan()在任何地方组合两个电话,那将非常酷.我的问题是:我们可以,怎么样?我对编译器的工作原理有所了解,但我不知道GCC在哪里可以执行这种优化.基本思想是每当有一对isnan()(或__builtin_isnan)调用OR在一起时,它应该同时ucomisd使用两个操作数发出一条指令.
编辑添加Basile Starynkevitch的答案提示的一些研究:
如果我使用-fdump-tree-all编译,我会发现两个似乎相关的文件.首先,*.gimple包含这个(以及更多):
D.2229 = x unord x;
D.2230 = y unord y;
D.2231 = D.2229 | D.2230;
Run Code Online (Sandbox Code Playgroud)
在这里我们可以清楚地看到GCC知道它会传递(x, x)给isunordered().如果我们想通过在此级别进行转换来进行优化,则规则大致为:"替换a unord a | b unord b为" a unord b.这是编译我的第二个C代码时得到的结果:
D.2229 = x unord y;
Run Code Online (Sandbox Code Playgroud)
另一个有趣的文件是*.original:
return <retval> = (int) (x unord x || y unord y);
Run Code Online (Sandbox Code Playgroud)
这实际上是由生成的整个非评论文件-fdump-tree-original.对于更好的源代码,它看起来像这样:
return <retval> = x unord y;
Run Code Online (Sandbox Code Playgroud)
显然,可以应用相同类型的转换(仅此处||而不是|).
但不幸的是,如果我们修改源代码,例如:
if (__builtin_isnan(x))
return true;
if (__builtin_isnan(y))
return true;
return false;
Run Code Online (Sandbox Code Playgroud)
然后我们得到了完全不同的Gimple和Original输出文件,尽管最终的程序集和以前一样.那么也许最好在管道的后期尝试这种转换?该*.optimized文件(以及其他)显示与原始版本的"if"s版本相同的代码,因此这很有希望.
Mar*_*sse 10
这种优化不仅是可能的,现在可以在gcc-6中找到:https: //gcc.gnu.org/viewcvs/gcc?view = revision&vision = 222077