使用 SIMD AVX 计算两个排序数组的对称差的大小
Computing size of symmetric difference of two sorted arrays using SIMD AVX
我正在寻找一种方法来优化我正在研究的算法。这是重复性最强的,因此计算密集型部分是比较任何大小的两个排序数组,包含唯一的无符号整数(uint32_t
)值,以获得它们的对称差的大小(仅存在于其中一个向量的元素数)。将部署算法的目标计算机使用支持 AVX2 的英特尔处理器,因此我正在寻找一种使用 SIMD 就地执行算法的方法。
有没有办法利用 AVX2 指令来获取两个无符号整数排序数组的对称差的大小?
由于两个数组都是排序的,因此使用 SIMD (AVX2) 实现此算法应该相当容易。您只需要同时遍历两个数组,然后在比较两个 8 个整数的向量时出现不匹配时,您需要解决不匹配问题,即计算差异,并让两个数组索引恢复相位,并继续直到到达其中一个数组的末尾。然后只需添加另一个数组中剩余元素的数量(如果有)即可获得最终计数。
除非你的数组很小(如 <=16 个元素),否则你需要使用额外的代码来转储不相等的元素,将两个排序的数组合并。
如果对称差的大小预计非常小,则使用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 分钟元素(总共): 获得 4.95ns 与 4.65ns ---稍差。
- (完全矢量化)成对比较 4 x 4 个元素,将比较结果提取到 16 位掩码中,通过完美哈希函数传递,使用 128 个条目 LUT 的_mm256_permutevar8x32_epi32对 8 个元素进行排序,检查连续元素是否有重复项,使用 _mm_movemask_ps + 16 个条目 LUT + _mm_shuffle_epi8 在最少 4 个元素中仅存储唯一元素: 获得 4.00ns 与 4.65ns ---略好。
这是包含解决方案的文件和具有完美哈希+ LUT生成器的文件。
附注:请注意,这里解决了集合交集的类似问题。该解决方案有点类似于我上面概述的第 2 点。
相关文章:
- 显示错误输出的简单数组排序程序
- C 使用单个函数对具有多种值类型的数组排序
- 2D数组排序,空格打乱顺序
- C 2D数组排序
- C++数组排序 - 将"bbba"和"0001"视为不正确排序的问题
- 数组排序功能
- CString 数组排序
- 简单的数组排序/检查 划分和征服版本
- C++ 中的多维数组排序
- 选择在++中对并行数组排序
- C++:二维指针数组排序:选择排序不适用于某些实例
- C++字符串数组排序
- C++中的指针数组排序算法
- 在VC++6中使用向量进行数组排序时出错,而在VC++2012中没有错误
- 将索引数组排序为主数组
- 数组排序、数组输入、数组输出
- C++数组排序,内存错误
- 在嵌入式系统上将数组排序功能从c++移植到c
- 基于其他int数组排序
- 多维数组排序c++奇怪行为