【问题标题】:Testing whether AVX register contains some equal integer numbers测试 AVX 寄存器是否包含一些相等的整数
【发布时间】:2017-11-18 09:47:48
【问题描述】:

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

例如:

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

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

如果可能,我想在 C++ 中执行此操作,但如有必要,我可以下拉到汇编中。

【问题讨论】:

  • “高效”的门槛是多少?可以,但是很烦人
  • @harold,阈值是它必须比没有 SIMD(按顺序)完成的速度更快。
  • 顺便说一下,这被称为“冲突检测”,而在 AVX2 之后的下一个东西,称为 AVX-512,有一个名为 CDI 的扩展名,用于此目的。具体来说,你想要vpconflictq
  • @IwillnotexistIdonotexist ,这是个好消息,我也需要它来进行冲突检测:如果某些目标共享数组索引,则无法执行向量操作。相反,我必须一个接一个地做。

标签: c++ x86 simd avx avx2


【解决方案1】:

使用 AVX512 (AVX512VL + AVX512CD),您可以使用专为此目的设计的 VPCONFLICTQ


对于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));
}

以前:

一种简单的“将所有内容与所有内容进行比较”的方法可以与一些随机播放一起使用,如下所示(未测试):

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));
}

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

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));
}

【讨论】:

  • 所以它是 8 个向量操作,而简单的成对比较解决方案需要 4*3/2 = 6 次比较...但是后者中的操作数在汇编中可能大大超过 6 个,因为数组偏移量。我猜,虽然向量运算在汇编中几乎映射到相同的位置。所以它需要测量。如果您不介意,我已将您的 AVX512 解决方案移至顶部。
  • @SergeRogatch 可能更显着的区别是必须以某种方式使用/合并比较的结果 - 例如 setcc 他们和 or 他们一起(然后分支我猜猜?),这将添加 6 个 setcc 和 5 或 2 个 or (如果使用高字节寄存器作弊,则为 2,因此还需要 3 个额外的重组 µops,那里没有免费的午餐)。这大约使标量版本的指令数增加了一倍
猜你喜欢
  • 2017-04-21
  • 1970-01-01
  • 2011-02-17
  • 2012-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
相关资源
最近更新 更多