其他分享
首页 > 其他分享> > c – 为什么icc会为一个简单的主要部件生成奇怪的装配?

c – 为什么icc会为一个简单的主要部件生成奇怪的装配?

作者:互联网

我有一个简单的program

int main()
{
    return 2*7;
}

GCC和clang优化启动时会生成2指令二进制,但icc会产生奇怪的输出.

     push      rbp                                           #2.1
     mov       rbp, rsp                                      #2.1
     and       rsp, -128                                     #2.1
     sub       rsp, 128                                      #2.1
     xor       esi, esi                                      #2.1
     mov       edi, 3                                        #2.1
     call      __intel_new_feature_proc_init                 #2.1
     stmxcsr   DWORD PTR [rsp]                               #2.1
     mov       eax, 14                                       #3.12
     or        DWORD PTR [rsp], 32832                        #2.1
     ldmxcsr   DWORD PTR [rsp]                               #2.1
     mov       rsp, rbp                                      #3.12
     pop       rbp                                           #3.12
     ret

解决方法:

我不知道为什么ICC选择将堆栈对齐2个缓存行:

and       rsp, -128                                     #2.1
sub       rsp, 128                                      #2.1

那很有意思. L2缓存有一个相邻行预取器,它喜欢将成对的行(在一个128字节的对齐组中)拉成L2.但是主要的堆栈帧通常不会被大量使用.在某些程序中可能会分配重要的变量. (这也解释了如何设置rbp,以保存旧的RSP,以便它可以在ANDing之后返回.gcc也会在与该堆栈对齐的函数中使用RBP创建堆栈帧.)

其余的是因为main()是特殊的,ICC默认启用-ffast-math. (这是英特尔“肮脏”的小秘密之一,让它可以开箱即用自动矢量化更多浮点代码.)

这包括将代码添加到main的顶部以设置MXCSR(SSE状态/控制寄存器)中的DAZ / FTZ位.有关这些位的更多信息,请参阅英特尔的x86手册,但它们并不复杂:

> DAZ:非正规为零:作为SSE ​​/ AVX指令的输入,非正规被视为零.
> FTZ:Flush To Zero:舍入SSE / AVX指令的结果时,将次正常结果刷新为零.

相关:SSE “denormals are zeros” option

(ISO C禁止程序调用main(),因此允许编译器在main本身而不是在CRT启动文件中放置一次运行的东西.gcc / clang和-ffast-math指定用于连接CRT启动文件中的链接设置MXCSR.但是当使用gcc / clang进行编译时,它只会影响代码生成,允许优化.即将FP add / mul视为关联,当不同的临时值意味着它实际上不是.这与设置完全无关DAZ / FTZ).

这里使用非正规作为次正规的同义词:具有最小指数的FP值和隐含前导位为0而不是1的有效数,即幅度小于FLT_MIN or DBL_MIN的值,最小可表示的归一化浮点数/双精度.

https://en.wikipedia.org/wiki/Denormal_number.

产生子正常结果的指令可能要慢得多:优化延迟,某些硬件中的快速路径假定规范化结果,如果结果无法规范化,则采用微代码辅助.使用perf stat -e fp_assist.any来计算此类事件.

来自Bruce Dawson的优秀系列FP文章:That’s Not Normal–the Performance of Odd Floats.另外:

> Why does changing 0.1f to 0 slow down performance by 10x?
> Avoiding denormal values in C++

Agner Fog做了一些测试(见他的microarch pdf),并报道了Haswell / Broadwell:

Underflow and subnormals

Subnormal numbers occur when floating point operations are close to
underflow. The handling of subnormal numbers is very costly in some
cases because the subnormal results are handled by microcode
exceptions.

The Haswell and Broadwell have a penalty of approximately 124 clock
cycles in all cases where an operation on normal numbers gives a
subnormal result. There is a similar penalty for a multiplication
between a normal and a subnormal number, regardless of whether the
result is normal or subnormal. There is no penalty for adding a normal
and a subnormal number, regardless of the result. There is no penalty
for overflow, underflow, infinity or not- a-number results.

The penalties for subnormal numbers are avoided if the “flush-to-zero”
mode and the “denormals-are-zero” mode are both set in the MXCSR
register.

所以在某些情况下,现代英特尔CPU即使在低于正常值的情况下也可以避免惩罚,但是

标签:icc,c,x86,assembly,code-generation
来源: https://codeday.me/bug/20190823/1697267.html