另请参阅我不久前在此答案中挖掘的一些 AVX512 直方图链接和信息。
我认为基本思想是分散无冲突的元素集,然后重新收集、重新处理和重新分散下一个无冲突的元素集。重复直到不再有冲突。
请注意,根据 ,重复索引的第一次出现是“无冲突”元素,vpconflictd
因此简单的重复循环会向前推进。
此过程中的步骤:
将vpconflictd
结果转换为可以与收集指令一起使用的掩码:(_mm512_testn_epi32_mask
如@veritas 建议的那样)针对全一的向量看起来很好,因为您需要反转它。你不能只对它自己进行测试。
删除已经完成的元素: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 一起使用。