Ser*_*tch 0 c++ algorithm floating-point x86-64 vectorization
考虑数字的排序(升序)数组double。为了数值稳定,应该对数组求和,就好像从头到尾迭代一样,将总和累加到某个变量中。
如何使用AVX2有效地向量化?
我研究了此方法用AVX指令完成水平向量求和的最快方法,但是将其缩放为数组(可能需要一些分治法)似乎很棘手,同时通过确保小数来保持浮点精度在将它们添加到更大数量之前进行汇总。
澄清1:我认为应该可以将例如前4个项目相加,然后将它们添加到接下来4个项目的合计中,以此类推。但是我更喜欢一种不会完全破坏稳定性的方法。
澄清2:内存不应成为瓶颈,因为该阵列位于L3高速缓存中(但不应位于L1 / L2高速缓存中,因为该阵列的各个部分是从不同的线程填充的)。我不想求助于Kahan求和,因为我认为真正重要的是运算数量,而Kahan求和将使它增加约4倍。
如果需要精度和并行度,请使用Kahan求和或另一种错误补偿技术来对和进行重新排序(将SIMD向量元素跨入具有多个累加器的步幅)。
正如双重快速求和-Evgeny Latkin指出的那样,如果您瓶颈在内存带宽上,那么经过错误补偿的总和并不比最大性能总和慢很多,因为CPU具有大量的计算吞吐量,而在简单并行化中就无法使用总结内存带宽瓶颈
另请参阅(针对的Google搜索结果kahan summation avx)
回复:您的想法:将4个数字组成的组按顺序求和将使您隐藏FP添加延迟,而标量添加生产瓶颈。
在向量内进行水平求和需要大量的改组,因此这是一个潜在的瓶颈。您可能会考虑加载a0 a1 a2 a3,然后改组以获取a0+a1 x a2+a3 x,然后(a0+a1) + (a2+a3)。你有Ryzen,对不对?最后一步只是vextractf128降至128b。这仍然是总共3个ADD运算符和3个混洗运算符,但指令少于标量或128b向量。
您总是会遇到一些舍入误差,但是添加相似大小的数字会将其最小化。
另请参见 Simd matmul程序给出不同的数值结果,以获取有关成对求和和简单有效SIMD的一些注释。
相加4个连续数与垂直添加4个SIMD向量之间的差异可能可以忽略不计。SIMD向量使您在数组中(SIMD向量宽度)有很小的进步。除非阵列增长得非常快,否则它们的幅度仍将基本相似。
您不需要水平求和直到最后仍然可以获得大部分收益。您可以维护1或2个SIMD向量累加器,同时使用更多的SIMD寄存器求和短期(可能是4个或8个SIMD向量),然后再添加到主累加器中。
实际上,将总数划分为更多方式(跨SIMD向量元素)意味着总数不会增加。因此,它可以准确地解决您要避免的问题,将水平求和到单个标量累加器实际上会使情况变得更糟,尤其是对于严格排序的数组。
通过无序执行,您不需要非常多的tmp累加器就可以完成这项工作,并且不需要将累加到主累加器中的FP-add延迟隐藏起来。您可以进行几组累加到一个新的tmp = _mm_load_ps()向量中,然后将其添加到总数中,OoO exec将重叠这些执行。因此,您的主循环不需要很大的展开系数。
但是它不应该太小,您不希望成为主累加器中添加项的瓶颈。您想成为FP-add吞吐量的瓶颈。(或者,如果您关心Broadwell / Haswell,而又不完全限制内存带宽,请在FMA中加入1.0乘数以充分利用吞吐量。)
例如,Skylake SIMD FP加法器具有4个周期的延迟,0.5个周期的吞吐量,因此对于单个累加器中的每个加法器,您都需要做至少7个加法,它们是短dep链的一部分。优选地,和/或优选地,具有两个长期累积器,以在调度中更好地吸收来自资源冲突的泡沫。