假设第二个循环中省略的花括号仅仅是一个错字,并且在for循环中有一个错字,并且您询问了乘法浮点数的问题,但是您的代码显示了整数数组,这样甚至不会获得很好的向量化如果编译器看到它。虽然编译器可能会将来自A和B的4个值加载为单个指令,并在一条指令中执行4次乘法,但代码会强制编译器提取4个产品中的每一个,然后依次对它们进行求和,并逐个获取SIMD寄存器中的值通常很慢。
如果在另一方面,你这样做
float A[100000];
float B[100000];
float C0=0, C1=0, C2=0, C3=0;
for (size_t i=0; i < 100000/4; i += 4)
{
C0 += A[i+0] * B[i+0];
C1 += A[i+1] * B[i+1];
C2 += A[i+2] * B[i+2];
C3 += A[i+3] * B[i+3];
}
float C = (C0 + C1) + (C2 + C3);
然后一个很好的编译器可以像现在vectorise这个它认为每个循环中它加载两个SIMD寄存器,它们相乘,则可以添加结果一个SIMD寄存器的总和,并且只提取这4个数字并且在最后总计它们。
矢量化编译可以用SIMD来做到这一点,它不会改变个别和的评估顺序(FP数学不是关联的)。由于这个原因,编译器通常不被允许改变FP数学的顺序(不是没有一些额外的标志允许它在技术上违反语言标准),所以上面的代码可以用SIMD指令精确地表示,并且运行得更快(事实上我会放松循环,因为乘法会成为瓶颈)。
这是SIMD的一个技巧,你必须理解,然后思考如何用矢量指令最好地实现该操作,然后编写代码来执行相同的操作序列,并希望编译器能够找到你已经完成了。
或者你也可以用intrinsics自己编写矢量指令,或者使用OpenMP或类似的方式来更明确地告诉编译器该做什么。
SIMD优于线程这种操作的优势在于,您正在单个内核中使用更多的硅片......因此您不会阻止另一个线程获取周期。在我们的计算网格中,我们通常在任何一台机器上运行多个单线程进程,以保持所有内核在任何时候都处于繁忙状态......在这种情况下,使用更多内核来实现这一点是一种虚假的经济,您只需要窃取另一个线程可能会有用地运行另一项工作。
来源
2017-08-01 15:55:26
Tim
那么,矢量化优化器可能会或者很多都不会优化任一循环,但在考试环境中,我期望看到更加明确的映射到底层架构,以阐明学生已经理解了系统的原理。特别是水平加法应该在最后进行最终折叠时作为矢量加法执行,特别是由于缺乏关联性,可能会禁止等效的浮点格式。为了避免优化器阻塞代码复杂性,阵列的显式对齐规范也不会受到影响。 – doynax
对于考试,我希望看到这个手工编码的目标架构的程序集,说实话。给定一个中途体面的编译器,尽管这个代码应该最终被优化(在过去做了一些类似的HPC矢量,并验证了GCC的汇编输出)。 OP应该明确地分解二进制文件并检查SIMD指令...... – madscientist159
同意,内在函数将成为这里的一种方式,并且优化编译器可以使用纠结的代码创造奇迹(相反,随机分解出于难理解的原因)。在这里最让我困扰的是提到了“单精度浮点值”,在这种情况下,将'int'替换为'float',剩下一个固有的串行依赖链,用于累积,除非使用像' - 快速数学“,并打破了过程中的大部分数字算法。老实说,除非使用并行累积缓冲区,否则我无法在考试中看到答案被接受。 – doynax