I have learned that some Intel/AMD CPUs can do simultanous multiply and add with SSE/AVX:
FLOPS per cycle for sandy-bridge and haswell SSE2/AVX/AVX2.

我想知道如何在代码中最好地做到这一点,我也想知道它在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))个吗,或者乘法和加法中使用的寄存器必须是独立的吗?

最后,这如何适用于FMA(与哈斯韦尔一起)?_mm_add_ps(sum, _mm_mul_ps(a1, b1))是否自动转换为单条FMA指令或微操作?

推荐答案

允许编译器融合单独的加法和乘法,即使这会改变最终结果(通过使其更准确).

FMA只有一个舍入(它有效地保持了内部临时乘法结果的无限精度),而ADD+MUL有两个舍入.

#pragma STDC FP_CONTRACT ONcompilers are allowed to have it ON by default有效时,IEEE和C标准允许这样做(但不是所有标准都这样).GCC默认签约到金融市场管理局(默认为-std=gnu*%,但不是-std=c*%,例如-std=c++14%).For Clang,只有-ffp-contract=fast才能启用.(在只启用#pragma的情况下,仅在单个表达式(如a+b*c)内启用,而不是跨单独的C++语句启用.)

这不同于严格浮点和松散浮点(或者用GCC的术语来说,-ffast-mathvs.-fno-fast-math),后者允许其他类型的优化.这是特殊的,因为FMA内部临时函数的无限精度;如果内部临时函数中有任何舍入,在严格的FP中是不允许的.

即使您启用了 slack 浮点,编译器仍然可能 Select 不融合,因为如果您已经在使用内部函数,编译器可能会期望您知道您在做什么.


因此,the best way为了确保您实际获得所需的FMA说明,您实际上使用了为它们提供的内部函数:

FMA3 Intrinsics:(AVX2-英特尔哈斯韦尔)

  • _mm_fmadd_pd(),_mm256_fmadd_pd()
  • _mm_fmadd_ps(),_mm256_fmadd_ps()
  • 还有无数的其他变体...

FMA4 Intrinsics:(XOP-AMD推土机)

  • _mm_macc_pd(), _mm256_macc_pd()
  • _mm_macc_ps(), _mm256_macc_ps()
  • 还有无数的其他变体...

C++相关问答推荐

为什么海湾合作委员会在共享对象中的. init_data的虚拟内存地址之前留出一个空白

当main函数调用被重构时,C函数给出错误的结果

C:scanf(%d&q;,...)输入只有一个减号

减法运算结果的平方的最快方法?

不会停在空格或换行符上的错误

非常大的数组的大小

在C++中通过空指针隐式访问常量变量的值

在为hashmap创建加载器时,我的存储桶指向它自己

Sizeof(&Q;字符串&Q;)的正确输出是什么?

在txt文件中找到指定的字符串,并从数字中减go 相同的值

不同出处的指针可以相等吗?

在C++中允许使用字符作为宏参数

如何将C中的两个字符串与从文件接收的字符串中的字符数进行比较

将多项式从文件.txt加载到终端时出现问题

如何使用空元素块声明指针数组

Malloc和对齐

C中2个数字的加法 - 简单的人类方法

OpenGL 中的非渐变 colored颜色 变化

文件指针引起的C程序分段错误

初始化动态分配的布尔二维数组的最佳方法是什么?