Jan*_*tke 22 c c++ optimization compilation compiler-optimization
我有四个身份函数,它们基本上什么都不做。只有乘法1才能通过 clang 优化为单个ret语句。
float id0(float x) {
return x + 1 - 1;
}
float id1(float x) {
return x + 0;
}
float id2(float x) {
return x * 2 / 2;
}
float id3(float x) {
return x * 1;
}
Run Code Online (Sandbox Code Playgroud)
以下编译器输出是:(clang 10, at -O3)
.LCPI0_0:
.long 1065353216 # float 1
.LCPI0_1:
.long 3212836864 # float -1
id0(float): # @id0(float)
addss xmm0, dword ptr [rip + .LCPI0_0]
addss xmm0, dword ptr [rip + .LCPI0_1]
ret
id1(float): # @id1(float)
xorps xmm1, xmm1
addss xmm0, xmm1
ret
.LCPI2_0:
.long 1056964608 # float 0.5
id2(float): # @id2(float)
addss xmm0, xmm0
mulss xmm0, dword ptr [rip + .LCPI2_0]
ret
id3(float): # @id3(float)
ret
Run Code Online (Sandbox Code Playgroud)
我可以理解为什么id0并且id2无法优化。他们增加了可能变成正无穷大的值,第二次操作不会把它改回来。
但是为什么不能id1优化呢?无穷大的加法将产生无穷大,任何常规数的加法将产生该数字,而加法NaN将产生NaN。那么为什么它不是像* 1.
“我有四个身份函数,它们基本上什么都不做。”
这不是真的。
对于浮点数x + 1 - 1is not equal x + 0,它是 equal (x + 1) - 1。因此,如果您有一个非常小的x例子,那么您将在该x + 1步骤中丢失非常小的部分,并且编译器无法知道这是否是您的意图。
在 的情况下,由于浮点精度x * 2 / 2,thex * 2也可能不准确,因此您在这里有类似的情况,编译器不知道您是否出于某种原因想要以x这种方式更改 的值。
所以这些是相等的:
float id0(float x) {
return x + (1. - 1.);
}
float id1(float x) {
return x + 0;
}
Run Code Online (Sandbox Code Playgroud)
这些将是相等的:
float id2(float x) {
return x * (2. / 2.);
}
float id3(float x) {
return x * 1;
}
Run Code Online (Sandbox Code Playgroud)
所需的行为肯定可以用另一种方式定义。但是正如Nelfeal已经提到的,必须使用显式激活此优化-ffast-math
启用快速数学模式。此选项允许编译器对浮点数学做出积极的、可能有损的假设。这些包括:
- 浮点数学遵循实数的常规代数规则(例如 + 和 * 是结合的,x/y == x * (1/y) 和 (a + b) * c == a * c + b * c) ,
- 浮点运算的操作数不等于 NaN 和 Inf,并且
- +0 和 -0 可以互换。
fast-math 是用于 clang 和 gcc 的一组标志(这里是 clang 列出的标志):
- -fno-honor-infinities
- -fno-honor-nans
- -fno-math-errno
- -有限数学
- -fassociative-数学
- -freciprocal-math
- -fno 有符号零
- -fno-trapping-math
- -ffp-合同=快