Lou*_*ltz 9 c++ floating-point x86-64 clang compiler-optimization
类似于问题gcc的ffast-math实际上做了什么?并且与Clang优化级别的SO问题有关,我想知道在实际条件下优化是做什么clang的-Ofast,以及它们是否与gcc完全不同,或者这是否依赖于编译器依赖于硬件.
根据clang优化级别的公认答案,-Ofast增加了-O3优化:-fno-signed-zeros -freciprocal-math -ffp-contract=fast -menable-unsafe-fp-math -menable-no-nans -menable-no-infs.这似乎完全与浮点数学相关.但是这些优化对于像C++这样的事物来说意味着什么呢?像英特尔酷睿i7这样的CPU上的浮点数通用数学函数以及这些差异有多可靠?
例如,实际上:
该代码std::isnan(std::numeric_limits<float>::infinity() * 0)返回真正的我-O3.我相信这是符合IEEE数学标准的结果.
随着-Ofast不过,我得到一个错误的返回值.此外,该操作(std::numeric_limits<float>::infinity() * 0) == 0.0f返回true.
我不知道这是否与gcc中看到的相同.我不清楚结果如何依赖于结构,也不清楚编译器如何依赖它们,也不清楚是否存在任何适用的标准-Ofast.
如果有人可能会产生类似于一组单元测试或代码公案来解决这个问题,那可能是理想的.我已经开始做这样的事情,但宁愿不重新发明轮子.
Mar*_*oom 16
描述每个标志如何影响每个数学函数将需要太多的工作,我将尝试为每个标志提供一个示例.
让你有责任看看每个人如何影响一个特定的功能.
-fno-signed-zeros假设您的代码不依赖于零的符号.
在FP算术中,零不是乘法的吸收元素:0·x = x·0≠0,因为零具有符号,因此例如-3·0 = -0≠0(其中0通常表示+0).
你可以在Godbolt上看到这个,其中乘以零只展开为常数零-Ofast
float f(float a)
{
return a*0;
}
;With -Ofast
f(float): # @f(float)
xorps xmm0, xmm0
ret
;With -O3
f(float): # @f(float)
xorps xmm1, xmm1
mulss xmm0, xmm1
ret
Run Code Online (Sandbox Code Playgroud)
-freciprocal-math使用倒数代替除数:a/b = a·(1/b).
由于FP精度的限制,等号实际上并不存在.
乘法比除法更快,参见Fog的表格.
另见why-is-freciprocal-math-unsafe-in-gcc?.
关于Godbolt的实例:
float f(float a){
return a/3;
}
;With -Ofast
.LCPI0_0:
.long 1051372203 # float 0.333333343
f(float): # @f(float)
mulss xmm0, dword ptr [rip + .LCPI0_0]
ret
;With -O3
.LCPI0_0:
.long 1077936128 # float 3
f(float): # @f(float)
divss xmm0, dword ptr [rip + .LCPI0_0]
ret
Run Code Online (Sandbox Code Playgroud)
-ffp-contract=fast启用FP表达式的收缩.
对于您可以在字段中应用的任何法律,收缩是一个总括性术语,可以简化表达.
例如,a*k/k = a.
但是,由于精度有限,配备有+和·的FP编号通常不是字段.
此标志允许编译器以正确性为代价收缩FP表达式.
关于Godbolt的实例:
float f(float a){
return a/3*3;
}
;With -Ofast
f(float): # @f(float)
ret
;With -O3
.LCPI0_0:
.long 1077936128 # float 3
f(float): # @f(float)
movss xmm1, dword ptr [rip + .LCPI0_0] # xmm1 = mem[0],zero,zero,zero
divss xmm0, xmm1
mulss xmm0, xmm1
ret
Run Code Online (Sandbox Code Playgroud)
-menable-unsafe-fp-math从更广泛的意义上说,上述的一种.
启用对IEEE数学做出不安全假设的优化(例如,该添加是关联的),或者可能不适用于所有输入范围.这些优化允许代码生成器使用一些原本不可用的指令(例如
fsin在X86上).
请参见本有关的误差精度fsin指令.
在Godbolt的实例,其中4被扩展为(2/sup>)2:
float f(float a){
return a*a*a*a;
}
f(float): # @f(float)
mulss xmm0, xmm0
mulss xmm0, xmm0
ret
f(float): # @f(float)
movaps xmm1, xmm0
mulss xmm1, xmm1
mulss xmm1, xmm0
mulss xmm1, xmm0
movaps xmm0, xmm1
ret
Run Code Online (Sandbox Code Playgroud)
-menable-no-nans假设代码不生成NaN值.
在我之前的回答中,我分析了ICC如何通过假设没有NaN来处理复数乘法.
大多数FP指令自动处理NaN.
但也有例外,例如比较,这可以在Godbolt的现场直播中看到
bool f(float a, float b){
return a<b;
}
;With -Ofast
f(float, float): # @f(float, float)
ucomiss xmm0, xmm1
setb al
ret
;With -O3
f(float, float): # @f(float, float)
ucomiss xmm1, xmm0
seta al
ret
Run Code Online (Sandbox Code Playgroud)
请注意,这两个版本不等同于-O3,其中一个被排除在外,a并且b是无序的,而另一个版本包含在true结果中.
虽然在这种情况下性能相同,但在复杂表达中,这种不对称性会导致不同的展开/优化.
-menable-no-infs就像上面的但是对于无穷大.
我无法在Godbolt中重现一个简单的例子,但三角函数需要仔细处理无穷大,特别是对于复数.
如果你浏览一个glibc实现的数学目录(例如sinc),你会看到许多检查在编译时应该省略-Ofast.
| 归档时间: |
|
| 查看次数: |
2830 次 |
| 最近记录: |