。
我喜欢知道如何在代码中做到这一点,我也想知道它在CPU中的内部完成。 我的意思是使用超级阶段的体系结构。 假设我想做一笔长的款项,例如SSE中的以下内容:
//sum = a1*b1 + a2*b2 + a3*b3 +... where a is a scalar and b is a SIMD vector (e.g. from matrix multiplication)
sum = _mm_set1_ps(0.0f);
a1 = _mm_set1_ps(a[0]);
b1 = _mm_load_ps(&b[0]);
sum = _mm_add_ps(sum, _mm_mul_ps(a1, b1));
a2 = _mm_set1_ps(a[1]);
b2 = _mm_load_ps(&b[4]);
sum = _mm_add_ps(sum, _mm_mul_ps(a2, b2));
a3 = _mm_set1_ps(a[2]);
b3 = _mm_load_ps(&b[8]);
sum = _mm_add_ps(sum, _mm_mul_ps(a3, b3));
...
我的问题是如何将其转换为同时乘积并添加? 数据可以取决于吗? 我的意思是CPU可以同时进行
_mm_add_ps(sum, _mm_mul_ps(a1, b1))
或在乘法中使用的寄存器,并且添加必须是独立的? lastly这如何适用于FMA(与Haswell)?
_mm_add_ps(sum, _mm_mul_ps(a1, b1))
自动转换为单个FMA指令或微操作吗?
允许编译器融合分离的添加和乘积,即使这会改变最终结果(通过使其更准确)。ANFMA只有一个圆形(它有效地保持了内部临时乘结果的无限精度),而添加 + mul具有两个。 当生效时,IEEE和C标准允许这样做,并且允许compilers默认情况下将其具有
#pragma STDC FP_CONTRACT ON
(但不是全部)。 GCC默认情况下将FMA收缩为FMA(默认的
ON
,但没有,例如对于clang
,它仅由-std=c*
-std=c++14
启用,仅在单个表达式中,例如
-ffp-contract=fast
,而不是跨单独的c ++语句。)。
这与严格的浮点(或以GCC术语为
#pragma
vs.a+b*c
)不同,这将允许其他类型的优化可能会根据输入值增加舍入误差。 这是特殊的,因为FMA内部临时性的无限精度。如果在内部临时性中根本有任何舍入,则在严格的FP中不允许使用。即使您启用了轻松的浮点,编译器可能仍然选择不融合,因为它可能希望您知道您已经在使用Interins。
so最好的方法确保您实际上获得所需的FMA指令是您实际上使用提供的固有信息:
FMA3内在:(AVX2 -Intel Haswell)
-ffast-math
,_
-fno-fast-math
_mm_fmadd_pd()
,mm256_fmadd_pd()
大约有其他变化...
FMA4内在:(XOP- AMD推土机)
_mm_fmadd_ps()
,
_mm256_fmadd_ps()
_mm_macc_pd()
,
_mm256_macc_pd()
I在GCC 5.3,Clang 3.7,ICC 13.0.1和MSVC 2015中测试了以下代码(编译器版本19.00)。
_mm_macc_ps()
_mm256_macc_ps()
生成
float mul_add(float a, float b, float c) {
return a*b + c;
}
__m256 mul_addv(__m256 a, __m256 b, __m256 c) {
return _mm256_add_ps(_mm256_mul_ps(a, b), c);
}
指令(例如
vfmadd
vfmadd213ss
)合同。以下编译器选项足以生成
mul_add
指令(使用MSVC除外)。
mul_addv
vfmadd
结果为零,即通过两个手动操作可以有点取消操作。
当我第一次测试此代码时,我认为编译器本身用FMA操作替换了手动操作。我在拆卸中检查了一下,没有做任何替代。