C++ 测试AVX寄存器是否包含一些相等的整数

C++ 测试AVX寄存器是否包含一些相等的整数,c++,x86,simd,avx,avx2,C++,X86,Simd,Avx,Avx2,考虑一个包含四个64位整数的256位寄存器。 在AVX/AVX2中是否有可能有效地测试这些整数中的一些是否相等 例如: a) {43,17,25,8}:结果必须是false,因为4个数字中没有2个相等 b) {47,17,23,17}:结果必须为“真”,因为数字17在AVX向量寄存器中出现2次 我想用C++做这个,如果可能的话,但是如果需要的话,我可以下拉到汇编。< P>用 AVX512(AVX512VL+AVX512CD),你将使用,这是为这个目的而设计的。 对于AVX2: 通过减少冗余比较

考虑一个包含四个64位整数的256位寄存器。 在AVX/AVX2中是否有可能有效地测试这些整数中的一些是否相等

例如:

a)
{43,17,25,8}
:结果必须是
false
,因为4个数字中没有2个相等

b)
{47,17,23,17}
:结果必须为“真”,因为数字
17
在AVX向量寄存器中出现2次


我想用C++做这个,如果可能的话,但是如果需要的话,我可以下拉到汇编。

< P>用<强> AVX512<强>(AVX512VL+AVX512CD),你将使用,这是为这个目的而设计的。
对于AVX2

通过减少冗余比较,减少了一些操作:

int test1(__m256i x)
{
    __m256i x0 = _mm256_permute4x64_epi64(x, 0x4B);
    // 1 0 2 3
    // 3 2 1 0
    __m256i e0 = _mm256_cmpeq_epi64(x0, x);
    __m256i x1 = _mm256_shuffle_epi32(x, 0x4E);
    // 2 3 0 1
    // 3 2 1 0
    __m256i e1 = _mm256_cmpeq_epi64(x1, x);
    __m256i t = _mm256_or_si256(e0, e1);
    return !_mm256_testz_si256(t, _mm256_set1_epi32(-1));
}

以前:

一种简单的“将所有内容与所有内容进行比较”的方法可以用于一些洗牌,类似于这样(未经测试):

GCC7将其编译成合理的代码,但Clang确实做了一些奇怪的事情。它似乎认为
vpor
没有256位版本(它完全没有)。在这种情况下,将ORs更改为加法的作用大致相同(将两个-1相加将不会为零),并且不会对Clang造成问题(也未测试):


“有效”的门槛是多少?这是可以做到的,但很简单annoying@harold,阈值是它必须比不使用SIMD(按顺序)时更快。顺便说一句,这称为“冲突检测”,AVX2之后的第二件事,称为AVX-512,有一个扩展名为CDI,用于此目的。具体来说,您需要
vpconflictq
@IwillnotexistIdonotexist,这是个好消息,我还需要它来检测冲突:如果某些目标共享数组索引,则无法执行向量操作。相反,我必须一个接一个地做。所以这是8个向量运算,而成对比较的简单解决方案需要4*3/2=6个比较。。。但是,由于阵列偏移,后者中的操作数在组装中可能大大超过6个。虽然向量操作在汇编中几乎映射到相同的操作,但我猜。所以它需要测量。如果您不介意的话,我已经将您的AVX512解决方案移到了顶部。@SergeRogatch可能更重要的区别在于,必须以某种方式使用/合并比较的结果-例如
setcc
它们和
它们都在一起(然后我猜是在它上面分支?),这将增加6个setcc和5个或2个(如果用高字节寄存器作弊,则为2个,因此还需要3个额外的重组µops,那里没有免费午餐)。这大约是标量版本指令计数的两倍
int hasDupe(__m256i x)
{
    __m256i x1 = _mm256_shuffle_epi32(x, 0x4E);
    __m256i x2 = _mm256_permute4x64_epi64(x, 0x4E);
    __m256i x3 = _mm256_shuffle_epi32(x2, 0x4E);
    // 2 3 0 1
    // 3 2 1 0
    __m256i e0 = _mm256_cmpeq_epi64(x1, x);
    // 1 0 3 2
    // 3 2 1 0
    __m256i e1 = _mm256_cmpeq_epi64(x2, x);
    // 0 1 2 3
    // 3 2 1 0
    __m256i e2 = _mm256_cmpeq_epi64(x3, x);
    __m256i t0 = _mm256_or_si256(_mm256_or_si256(e0, e1), e2);
    return !_mm256_testz_si256(t0, _mm256_set1_epi32(-1));
}
int hasDupe(__m256i x)
{
    __m256i x1 = _mm256_shuffle_epi32(x, 0x4E);
    __m256i x2 = _mm256_permute4x64_epi64(x, 0x4E);
    __m256i x3 = _mm256_shuffle_epi32(x2, 0x4E);
    // 2 3 0 1
    // 3 2 1 0
    __m256i e0 = _mm256_cmpeq_epi64(x1, x);
    // 1 0 3 2
    // 3 2 1 0
    __m256i e1 = _mm256_cmpeq_epi64(x2, x);
    // 0 1 2 3
    // 3 2 1 0
    __m256i e2 = _mm256_cmpeq_epi64(x3, x);
    // "OR" results, workaround for Clang being weird
    __m256i t0 = _mm256_add_epi64(_mm256_add_epi64(e0, e1), e2);
    return !_mm256_testz_si256(t0, _mm256_set1_epi32(-1));
}