使用 SIMD AVX 计算两个排序数组的对称差的大小
Posted
技术标签:
【中文标题】使用 SIMD AVX 计算两个排序数组的对称差的大小【英文标题】:Computing size of symmetric difference of two sorted arrays using SIMD AVX 【发布时间】:2017-04-06 10:51:10 【问题描述】:我正在寻找一种方法来优化我正在研究的算法。它是最重复的,因此计算密集的部分是比较任何大小的两个排序数组,包含唯一的无符号整数 (uint32_t
) 值,以获得它们的对称差异的大小(仅存在于其中之一中的元素数量)向量)。将部署算法的目标机器使用支持 AVX2 的英特尔处理器,因此我正在寻找一种使用 SIMD 就地执行它的方法。
有没有办法利用 AVX2 指令来获得两个无符号整数的排序数组的对称差的大小?
【问题讨论】:
一般问题不被接受的原因是答案太模糊而无用:“当然,可能有一种方法可以使用 AVX 来加速这样的操作,但听起来很有挑战性。 "在不知道集合的一般大小、数据类型(整数?浮点数?字符串?)、值的范围、数据结构等的情况下,无话可说。 @Peter 谢谢你的评论。我编辑了这个问题,我希望现在更准确。这些集合基本上是具有任意大小的唯一无符号整数数组。 @PaulR 谢谢,我是AVX2
,我会编辑帖子。
【参考方案1】:
由于两个数组都已排序,因此使用 SIMD (AVX2) 实现此算法应该相当容易。您只需要同时遍历两个数组,然后当比较两个 8 个整数的向量时出现不匹配时,您需要解决不匹配问题,即计算差异,并使两个数组索引恢复同相,并且继续,直到您到达其中一个数组的末尾。然后只需添加另一个数组中剩余元素的数量(如果有)即可获得最终计数。
【讨论】:
感谢您的回答!这就是我的算法现在的工作方式——除了它比较单个标量,而不是向量。我猜只有在遇到 8 个整数的匹配向量时,使用向量进行比较才有用——这不是最常见的情况。因此,我正在寻找一些替代方案。 是的,我应该说它的效率将取决于不匹配的稀疏程度——如果它们以如此高的速度发生,以至于几乎每个向量比较都发现不匹配,那么你重新可能不会从 SIMD 中获得胜利。 (对于相对稀疏的不匹配(大多数向量匹配)虽然这可能是一个巨大的胜利。)【参考方案2】:除非您的数组很小(例如
如果预计对称差的大小非常小,则使用 PaulR 描述的方法。 如果预计尺寸很大(如元素总数的 10%),那么矢量化它会遇到很大的麻烦。优化标量解决方案要容易得多。
写了好几个版本的代码,对我来说最快的是:
int Merge3(const int *aArr, int aCnt, const int *bArr, int bCnt, int *dst)
int i = 0, j = 0, k = 0;
while (i < aCnt - 32 && j < bCnt - 32)
for (int t = 0; t < 32; t++)
int aX = aArr[i], bX = bArr[j];
dst[k] = (aX < bX ? aX : bX);
k += (aX != bX);
i += (aX <= bX);
j += (aX >= bX);
while (i < aCnt && j < bCnt)
... //use simple code to merge tails
这里的主要优化是:
-
在块中执行合并迭代(每个块 32 次迭代)。这允许将停止标准从
(i < aCnt && j < bCnt)
简化为t < 32
。大部分元素都可以这样做,尾部可以用慢代码处理。
以无分支方式执行迭代。注意三元运算符编译成cmov
指令,比较编译成setXX
指令,所以循环体中没有分支。输出数据使用众所周知的技巧存储:写入所有元素,但只为有效元素增加指针。
我还尝试了什么:
-
(无向量化)执行 (4 + 4) 双调合并,检查连续元素是否重复,移动指针以便跳过 4 min 元素(总共):
得到 4.95ns 和 4.65ns --- 稍微差一点。
(完全矢量化)成对比较 4 x 4 个元素,将比较结果提取到 16 位掩码中,通过完美哈希函数传递,使用 _mm256_permutevar8x32_epi32 和 128 项 LUT 得到排序的 8 个元素,检查连续元素是否重复,使用_mm_movemask_ps + 16-entry LUT + _mm_shuffle_epi8 仅存储最少 4 个元素中的唯一元素:获得 4.00ns 与 4.65ns --- 略好。
这里是file with solutions 和file with perfect hash + LUT generator。
附:请注意,集合交集的类似问题已解决here。该解决方案与我在上面第 2 点中概述的有点相似。
【讨论】:
以上是关于使用 SIMD AVX 计算两个排序数组的对称差的大小的主要内容,如果未能解决你的问题,请参考以下文章
SIMD (AVX2) - 将 uint8_t 值加载到多个浮点 __m256 寄存器
SIMD,SSE,AVX - 掩码 8 浮动无符号字符? [复制]