使用 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 &lt; aCnt &amp;&amp; j &lt; bCnt) 简化为t &lt; 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 需要置换?

SIMD,SSE,AVX - 掩码 8 浮动无符号字符? [复制]

使用 AVX2 计算 8 个长整数的最小值

SIMD指令集——一条指令操作多个数,SSE,AVX都是,例如:乘累加,Shuffle等

为啥内核中不使用 SIMD 指令?