使用 SIMD 指令将代码转换为代码
Posted
技术标签:
【中文标题】使用 SIMD 指令将代码转换为代码【英文标题】:Turn the code into a code using SIMD instructions 【发布时间】:2017-07-31 08:28:22 【问题描述】:我正在准备考试,并且正在做一些不方便的练习。因此,我一直在提供此代码,并想知道是否已将代码转换为 SIMD 指令。
代码
int A[100000];
int B[100000];
int C=0;
for int(i=0; i < 100000; i++)
C += A[i] * B[i];
既然没有余数,我们就不用管它了。我们还假设它是一个 128 位的寄存器,因此可以计算 4 个单精度浮点值。
我的结果 - 使用 SIMD
int A[100000];
int B[100000];
int C=0;
for int(i=0; i < 100000/4; i += 4)
C += A[i] * B[i];
C += A[i+1] * B[i+1];
C += A[i+2] * B[i+2];
C += A[i+3] * B[i+3];
您认为使用 SIMD 指令而不是编写多线程程序有什么优势?
【问题讨论】:
【参考方案1】:假设您的第二个循环中省略的花括号只是一个错字,并且 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);
然后一个好的编译器可以向量化它,因为现在它看到在每个循环中它加载两个 SIMD 寄存器,将它们相乘,然后它可以将结果添加到总和的 SIMD 寄存器中,并且只提取这 4 个总和并将它们相加到最后。
矢量化编译可以使用 SIMD 执行此操作,并且不会更改单个总和的评估顺序(FP 数学不是关联的)。出于这个原因,通常不允许编译器更改 FP 数学的顺序(并非没有一些额外的标志,允许它在技术上违反语言标准),因此上面的代码可以用 SIMD 指令精确表示,并且运行得更快(事实上,我会进一步展开循环,因为乘法将成为目前的瓶颈)。
这是 SIMD 的一种技巧,您必须了解并思考如何最好地使用向量指令实现该操作,然后编写代码来执行相同的操作序列,并希望编译器发现您的内容已经完成了。
或者您可以使用内在函数自己编写向量指令,或使用 OpenMP 或类似工具更明确地告诉编译器要做什么。
在这种操作中,SIMD 优于线程的优势之一在于,您可以在单个内核中使用更多的芯片......因此您不会阻止另一个线程获得周期。在我们的计算网格上,我们通常在任何一台机器上运行许多单线程进程,以使所有内核始终处于忙碌状态……在这种情况下,使用更多内核进行这个求和是一种虚假的经济,你只是在窃取循环另一个线程可能有用地运行另一个作业。
【讨论】:
【参考方案2】:是的,所提供的代码应该使用有能力的 CPU 和编译器编译成 SIMD 指令。
在支持向量的处理器上,SIMD 提供了可大大加速相同并行计算的硬件功能。例如,由于流式 RAM 访问,SIMD 通常会更好地利用单个内核上的缓存,假设正在处理的数据位于内存的连续区域中。使用多处理、缓存竞争和其他同步开销实际上会降低性能,因为各个内核会尝试同时写入数据。这是对冯诺依曼机器的内在提升,因为只需从共享系统内存中读取一条而不是四条单独的指令。
并行执行这些算术运算的逻辑始终存在,但需要使用特定的 SIMD 指令。因此,SIMD 倾向于用于热循环中,在这种循环中,手动调整对整体优化有意义。
【讨论】:
好吧,矢量化优化器可能会或许多不会优化任一循环,但在考试环境中,我希望看到更明确的映射到底层架构,以澄清学生已经理解系统的原理.特别是水平加法应该作为向量加法执行,最后折叠,特别是由于缺乏关联性,可能禁止等效浮点情况。数组的显式对齐规范也不会损害避免结束代码复杂性阻止优化器。 老实说,对于考试,我更希望看到这个针对目标架构的手工编码。给定一个体面的编译器,尽管这段代码最终应该被优化(过去为 HPC 做过一些类似的向量工作,并验证了来自 GCC 的汇编输出)。 OP 绝对应该反汇编二进制文件并检查 SIMD 指令... 同意,内在函数将是这里的方法,优化编译器可以用纠结的代码创造奇迹(相反,由于晦涩的原因随机分解)。这里最困扰我的是提到single precision floating point values
在这种情况下,即用int
代替float
,除非使用像-ffast-math
这样的可怕选项并破坏大多数过程中的数值算法。老实说,除非使用并行累积缓冲区,否则我看不到答案在考试中被接受。以上是关于使用 SIMD 指令将代码转换为代码的主要内容,如果未能解决你的问题,请参考以下文章
使用 iPhone 的 SIMD 浮点单元将浮点数转换为整数
SSE 指令中的 UnsignedSaturate 是啥意思?
ARMv8 SIMD和浮点指令编程Libyuv I420 转 ARGB 流程分析
将 simd_quatf 转换为 SCNQuaternion