AVX512-CD的使用

Usage of AVX512-CD

我目前正在与 KNL 合作,并尝试了解 AVX512 的新机遇。除了扩展寄存器方面,AVX512 还带有新的指令集。冲突检测似乎很有希望。 内在的

_mm512_conflict_epi32(...)

创建一个向量寄存器,包含给定源寄存器的无冲突子集:

如您所见,第一次出现的值会导致结果向量中相应位置出现 0。如果该值出现多次,则结果寄存器保存零扩展值。 到目前为止,一切都很好!但我想知道如何利用这个结果进行进一步的聚合或计算。我读到可以将它与前导零计数一起使用,但我认为这不足以确定子集的值。

有谁知道如何利用这一结果?

此致

现在我明白你的问题是如何利用 VPCONFLICTD/Q 的结果来构建子集以进行进一步的聚合或计算...

使用你自己的例子:

conflict_input  = 
   [
  00000001|00000001|00000001|00000001|
  00000002|00000002|00000002|00000002|
  00000002|00000002|00000001|00000001|
  00000001|00000001|00000001|00000001
   ]

应用VPCONFLICTD:

__m512i out = _mm512_conflict_epi32(in);

现在我们得到:

conflict_output = 
  [
  00000000|00000001|00000003|00000007|
  00000000|00000010|00000030|00000070|
  000000f0|000001f0|0000000f|0000040f|
  00000c0f|00001c0f|00003c0f|00007c0f
  ]
bit representation = 
  [
  ................|...............1|..............11|.............111|
  ................|...........1....|..........11....|.........111....|
  ........1111....|.......11111....|............1111|.....1......1111|
  ....11......1111|...111......1111|..1111......1111|.11111......1111
  ]

如果您希望根据非重复值的首次出现获得掩码

const   __m512i set1 = _mm512_set1_epi32(0xFFFFFFFF);
const __mmask16 mask = _mm512_testn_epi32_mask(out, set1);

现在您可以使用 mmask16

执行所有常用操作
[1000100000000000]

你也可以压缩它:

const __m512i out3 = _mm512_mask_compress_epi32(set0, mask, in);

[00000001|00000002|00000000|00000000|
 00000000|00000000|00000000|00000000|
 00000000|00000000|00000000|00000000|
 00000000|00000000|00000000|00000000]

面具可以做很多事情;但是,我有趣地注意到 vplzcntd 并且不知道在哪里可以使用它:

const __m512i out1 = _mm512_conflict_epi32(in);
const __m512i out2 = _mm512_lzcnt_epi32(out1);

output2 = [
00000020|0000001f|0000001e|0000001d|
00000020|0000001b|0000001a|00000019|
00000018|00000017|0000001c|00000015|
00000014|00000013|00000012|00000011
          ]
        = [
..........1.....|...........11111|...........1111.|...........111.1|
..........1.....|...........11.11|...........11.1.|...........11..1|
...........11...|...........1.111|...........111..|...........1.1.1|
...........1.1..|...........1..11|...........1..1.|...........1...1
          ]

另见一些 AVX512 直方图链接和我不久前在 中找到的信息。

我认为基本思路是将无冲突的元素集打散,然后重新聚集,重新处理,重新打散下一个无冲突的元素集。重复直到不再有冲突。

请注意,根据 vpconflictd,重复索引的第一个出现是 "conflict-free" 元素,因此简单的重复循环会向前推进。

此过程中的步骤:

  1. vpconflictd 结果转换为可以与收集指令一起使用的掩码:_mm512_testn_epi32_mask(如@veritas 所建议)针对全一外观向量这很好,因为你需要反转它。你不能只针对它自己进行测试。

  2. 删除已经完成的元素:vpcompressd 可能对此有好处。我们甚至可以用新元素填充向量中的 "empty" 空间,这样我们就不会重新 运行 gather / process / scatter 循环,大部分元素都被屏蔽了。

例如,这个 可能 用作直方图循环,如果我这样做正确的话:

// probably slow, since it assumes conflicts and has a long loop-carried dep chain
// TOTALLY untested.
__m512i all_ones = _mm512_set1_epi32(-1);  // easy to gen on the fly (vpternlogd)
__m512i indices = _mm512_loadu_si512(p);
p += 16;

// pessimistic loop that assumes conflicts
while (p < endp) {
    // unmasked gather, so it can run in parallel with conflict detection
    __m512i v = _mm512_i32gather_epi32(indices, base, 4);
    v = _mm512_sub_epi32(gather, all_ones);              // -= -1 to reuse the constant.

    // scatter the no-conflict elements
    __m512i conflicts = _mm512_conflict_epi32(indices);
    __mmask16 knoconflict = _mm512_testn_epi32_mask(conflicts, all_ones);
    _mm512_mask_i32scatter_epi32(base, knoconflict, indices, v, 4);

    // if(knoconflict == 0xffff) { goto optimistic_loop; }

    // keep the conflicting elements and merge in new indices to refill the vector
    size_t done = _popcnt32(knoconflict);
    p += done;                 // the elements that overlap will be replaced with the conflicts from last time
    __m512i newidx = _mm512_loadu_si512(p);
    // merge-mask into the bottom of the newly-loaded index vector
    indices = _mm512_mask_compress_epi32(newidx, ~knoconflict, indices);
}

我们最终都需要掩码(knoconflict~knoconflict)。最好使用 _mm512_test_epi32_mask(same,same) 并避免需要 testn 反对的向量常量。通过将掩码的反转放在 scatter 依赖链上,这可能会缩短 mask_compress 中索引的循环携带依赖链。当没有冲突时(包括迭代之间),散点是独立的。

如果冲突很少见,最好分支一下。这种无分支的冲突处理有点像在循环中使用 cmov:它创建了一个长循环携带的依赖链。

分支预测 + 推测执行会打破这些链条,并允许多个聚集/分散同时进行。 (并且在没有冲突的情况下完全避免 运行ning popcnt / vpcompressd)。

另请注意,vpconflictd 在 Skylake-avx512 上运行缓慢(但在 KNL 上则不然)。当您预计冲突非常罕见时,您甚至可以使用快速 any_conflicts() 检查,在 运行 冲突处理之前不会发现冲突的位置。

参见 for a ymm AVX2 implementation, which should be faster than Skylake-AVX512's micro-coded vpconflictd ymm. Expanding it to 512b zmm vectors shouldn't be difficult (and might be even more efficient if you can take advantage of AVX512 masked-compare into mask to replace a boolean operation between two compare results). Maybe with AVX512 vpcmpud k0{k1}, zmm0, zmm1 with a NEQ predicate