Clang编译AVX浮点SIMD代码时为何额外插入FMA指令?
额外FMA指令的作用
vrcpps是x86提供的快速单精度倒数近似指令,硬件直接给出的近似倒数只有约1112位有效精度,远达不到单精度浮点数23位尾数的精度要求。Clang生成的两条FMA指令是执行**1次牛顿-拉夫逊迭代修正**,把倒数近似值的精度提升到接近原生浮点除法的水平,误差控制在12个ulp以内,满足-Ofast模式下的精度要求。
把指令逻辑按计算流程拆解开就很清楚:
- 计算到
vrcpps ymm2, ymm0时,ymm2存的是分母x = num+1400的粗略倒数r0 ≈ 1/x - 接下来
vmulps ymm3, ymm1, ymm2算出的是粗略商值q0 = 278*num * r0 ≈ 278*num/x - 第一条FMA
vfmsub213ps ymm0, ymm3, ymm1计算商值的误差项:err = q0 * x - 278*num。如果q0是完全精确的,err应该为0,err的大小直接反映q0和精确值的偏差 - 第二条FMA
vfnmadd213ps ymm0, ymm2, ymm3执行迭代修正:q = q0 - r0 * err,修正后的q就是精度达标的最终结果。
这种实现是-Ofast开启-freciprocal-math优化后的默认选择:允许用倒数近似+少量迭代替换原生除法,在精度损失极小的前提下换更高性能。
与
vdivps实现的性能对比 性能对比基于Skylake微架构的公开实测指令参数:
- 普通向量算术指令(
vaddps/vmulps/FMA类指令):单条延迟4周期,每周期可发射2条,跑在CPU的通用向量执行端口 vrcpps(YMM):延迟4周期,每周期可发射1条vdivps(YMM单精度除法):延迟11周期,每11周期才能发射1条,跑在独立的慢速除法执行单元,和普通向量指令不共享执行资源
两种实现的实际表现差异:
- 单次调用端到端延迟:原生
vdivps版本总延迟约15周期(4周期算分母+11周期除法),rcp+迭代版本总延迟约20周期,单次顺序执行时vdivps延迟略低。 - 稳态吞吐量(批量计算场景):rcp+迭代版本的所有指令都使用高带宽的通用向量执行单元,指令间可充分重叠执行,稳态下每67周期就能完成一组8个float的计算;而`vdivps`受限于除法单元的低吞吐量,每11周期才能完成一组计算,批量处理时rcp+迭代版本性能是`vdivps`版本的1.52倍。
- 精度层面,经过1次牛顿迭代修正的rcp计算结果和
vdivps的结果偏差通常在1~2个ulp,绝大多数工程场景下完全可用。
补充说明:-O3模式默认不开启快速浮点优化,必须严格遵循IEEE 754浮点规范,因此会直接使用vdivps指令,不会采用rcp近似方案。
内容的提问来源于stack exchange,提问作者HesLg
相关产品推荐
相关产品推荐

