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

问题描述 投票:3回答:1

我有一个简单的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
c++ assembly x86 code-generation icc
1个回答
6
投票

我不知道为什么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:刷新为零:当舍入SSE / AVX指令的结果时,将次正常结果刷新为零。

相关:SSE "denormals are zeros" option

(ISO C ++禁止一个程序从回调到main(),因此允许编译器在main本身而不是CRT启动文件中运行一次.gcc / clang与-ffast-math指定用于链接设置MXCSR的CRT启动文件中的链接。但是当使用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来计算此类事件。

来自布鲁斯道森的优秀系列FP文章:That’s Not Normal–the Performance of Odd Floats。也:

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

下溢和次正常

当浮点运算接近下溢时,会出现次正规数。在某些情况下,处理次正规数非常昂贵,因为次正规结果由微代码异常处理。

Haswell和Broadwell在正常数字运算产生低于正常结果的所有情况下都会有大约124个时钟周期的代价。无论结果是正常还是低于正常,对于正常数和次正规数之间的乘法都存在类似的惩罚。无论结果如何,添加正常数和次正规数都不会受到惩罚。溢出,下溢,无穷大或非数字结果不会受到惩罚。

如果在MXCSR寄存器中设置“清零到零”模式和“非正规为零”模式,则可以避免对次正规数的处罚。

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

© www.soinside.com 2019 - 2024. All rights reserved.