我正在寻找一种优化我正在研究的算法的方法。它是最重复的,因此计算密集型部分是两个任意大小的排序数组的比较,包含唯一的无符号整数(uint32_t
)值,以获得它们的对称差异的大小(元素的数量)仅存在于其中一个向量中)。将部署算法的目标机器使用支持AVX2的Intel处理器,因此我正在寻找一种使用SIMD就地执行它的方法。
有没有办法利用AVX2指令来获得两个无符号整数排序数组的对称差异大小?
答案 0 :(得分:3)
由于两个数组都已排序,因此使用SIMD(AVX2)实现此算法应该相当容易。您只需要同时迭代两个数组,然后当比较两个8个向量的向量时出现不匹配时,您需要解决不匹配问题,即计算差异,并使两个数组索引同步回来,并且继续,直到你到达其中一个数组的末尾。然后只需在其他数组中添加剩余元素(如果有),以获得最终计数。
答案 1 :(得分:1)
除非您的数组很小(例如< = 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
这里的主要优化是:
(i < aCnt && j < bCnt)
到t < 32
的停止标准。这可以针对大多数元素完成,并且可以使用慢速代码处理尾部。cmov
指令,并且比较被编译为setXX
指令,因此循环体中没有分支。输出数据与众所周知的技巧一起存储:写入所有元素,但仅为有效元素增加指针。我还尝试了什么:
以下是file with solutions和file with perfect hash + LUT generator。
P.S。请注意,解决了集合交集的类似问题here。解决方案有点类似于我在上面的第2点所概述的内容。