1

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

_mm512_conflict_epi32(...)

创建一个向量寄存器,包含给定源寄存器的无冲突子集:在此处输入图像描述

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

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

真挚地

4

2 回答 2

3

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

使用您自己的示例:

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

应用VPCNFLICTD

__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
          ]
于 2017-08-23T00:38:09.977 回答
1

另请参阅我不久前在此答案中挖掘的一些 AVX512 直方图链接和信息。

我认为基本思想是分散无冲突的元素集,然后重新收集、重新处理和重新分散下一个无冲突的元素集。重复直到不再有冲突。

请注意,根据 ,重复索引的第一次出现是“无冲突​​”元素,vpconflictd因此简单的重复循环会向前推进。

此过程中的步骤:

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

  2. 删除已经完成的元素:vpcompressd可能对此有好处。我们甚至可以用新元素填充向量中的“空白”空间,因此我们不会在大多数元素被屏蔽的情况下重新运行收集/处理/分散循环。

例如,如果我做得对,这可能作为直方图循环工作:

// 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在循环中使用:它创建了一个长的循环携带的依赖链。

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

另请注意,这vpconflictd在 Skylake-avx512 上速度很慢(但在 KNL 上却没有)。当您预计冲突非常罕见时,您甚至可以使用快速any_conflicts()检查,在运行冲突处理之前不会找出它们的位置。

有关 AVX2 实现,请参阅AVX2中冲突检测的后备实现ymm,它应该比 Skylake-AVX512 的 micro-coded 更快vpconflictd ymm。将其扩展到 512b zmm 向量应该不难(如果您可以利用 AVX512 masked-compare into mask 来替换两个比较结果之间的布尔运算,可能会更有效)。也许与vpcmpud k0{k1}, zmm0, zmm1带有 NEQ 谓词的 AVX512 一起使用。

于 2017-08-26T03:49:51.803 回答