我有一个简单的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
答案 0 :(得分:6)
我不知道为什么ICC选择按2条缓存行对齐堆栈:
and rsp, -128 #2.1
sub rsp, 128 #2.1
这很有趣。 L2缓存具有一个相邻行的预取器,该预取器喜欢将成对的线(在128字节对齐的组中)拉入L2。但是main的堆栈框架通常不被大量使用。某些程序中可能在其中分配了重要的变量。 (这也说明了设置rbp
来保存旧的RSP,以便它在ANDing之后可以返回。gcc还在函数中使用RBP生成堆栈帧,并在其中对齐该堆栈。)
剩下的是因为main()
很特殊,并且ICC默认启用-ffast-math
。 (这是Intel的“肮脏”小秘密之一,它可以自动将更多浮点代码向量化。)
这包括将代码添加到main
的顶部以设置MXCSR(SSE状态/控制寄存器)中的DAZ / FTZ位。有关这些位的更多信息,请参阅Intel的x86手册,但它们实际上并不复杂:
DAZ:异常为零:作为SSE / AVX指令的输入,异常被视为零。
FTZ:刷新为零:舍入SSE / AVX指令的结果时,次标准结果刷新为零。
相关:SSE "denormals are zeros" option
( ISO C ++禁止程序调用{{1}},因此允许编译器将运行一次的内容放到main()
本身而不是CRT启动文件中。指定gcc / clang与main
来链接设置MXCSR的CRT启动文件中的链接,但是当使用gcc / clang进行编译时,它仅影响允许优化的代码生成,即处理FP添加/ mul作为关联,当不同的临时人员实际上不是这样时。这与设置DAZ / FTZ完全无关。
此处,反常数用作次正数的同义词:FP值具有最小的指数和一个有效位,其中隐含的前导位是0而不是1。即,值小于FLT_MIN
or DBL_MIN
(最小的值)可表示的归一化浮点/双精度。
https://en.wikipedia.org/wiki/Denormal_number。
产生不正常结果的指令可能会慢得多:为了优化延迟,某些硬件中的快速路径采用了标准化的结果,如果结果无法进行标准化,则采用微码辅助。使用-ffast-math
对此类事件进行计数。
摘自Bruce Dawson出色的FP系列文章:That’s Not Normal–the Performance of Odd Floats 。另外:
Agner Fog做了一些测试(请参阅他的microarch pdf),并为Haswell / Broadwell报告:
下溢和次常态
浮点运算接近时出现次正规数 下溢。在某些情况下,处理非正规数非常昂贵 情况是因为次标准结果由微码处理 例外。
Haswell和Broadwell的罚款约为124个时钟 在所有情况下,对普通数字进行运算得到一个 次正常的结果。乘法也有类似的惩罚 介于正常数和次正常数之间,无论是否 结果是正常还是次正常。添加法线不收取任何罚款 和一个次正规数,无论结果如何。没有罚款 对于上溢,下溢,无穷大或非整数结果。
如果“刷新为零”,则避免了对次普通数的惩罚 模式和“反常态为零”模式都在MXCSR中设置 注册。
因此,在某些情况下,现代的Intel CPU甚至避免使用次标准的惩罚,但是