用X64 Simd换nibble
我知道 byte shuffling 指令,但是我想对Nibbles做同样的事情(4位值),我想用一个64位单词将16个小吃洗净。我的洗牌索引也被存储为16个小吃。最有效的实施是什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
我知道 byte shuffling 指令,但是我想对Nibbles做同样的事情(4位值),我想用一个64位单词将16个小吃洗净。我的洗牌索引也被存储为16个小吃。最有效的实施是什么?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(2)
使用以这种方式存储的控制向量的任意洗牌? gh,很难与之合作。我想您必须将两者都解开以喂食SSSE3
PSHUFB
,然后重新包装结果。可能只是
punpcklbw
针对右移副本,然后掩盖以将每个字节中的低4位保留。然后PSHUFB
。有时,奇数/偶数比扩大每个元素要容易(因此位仅留在其原始字节或单词中)。在这种情况下,如果我们可以更改您的nibble索引编号,则
punpcklqdq
可能会将奇数甚至nibbles放在高处,准备将它们放回原处。但是没有这样做,重新包装是一个单独的问题。我想将相邻的字节对组合到低字节中的单词中,也许如果吞吐量比潜伏期更重要。然后,您可以
packuswd
(反对零或自身)或
PSHUFB
(具有恒定控制向量)。如果您进行了多次这样的混乱,则可以将两个向量打包到一个,以存储
movhps
/movq
。使用AVX2,可能会使所有其他指令在两个128位车道中的两个独立的散装上工作。用
0x0f
掩盖数据(而不是之后),可以在CPU上使用两个随机拨动单元进行更多的ILP。至少如果它们已经在向量寄存器中具有UINT64_T值,或者数据和控制值来自内存,因此可以将两者都加载在同一周期中。如果来自GPRS,则为vmovq XMM的1/时钟吞吐量,reg
意味着DEP链之间存在资源冲突,因此它们都不能在同一周期中开始。但是,由于我们的数据可能在控制之前就已经准备就绪,因此提早掩盖它会使它远离控制延迟的关键路径。如果延迟是瓶颈而不是通常的吞吐量,请考虑用右移 pmaddubsw pmaddubsw 和/and/pack替换。或
pshufb
在奇数字节中忽略垃圾时打包。由于无论如何您都需要另一个常数,因此也可能使其成为pshufb
常数,而不是和
。如果您有AVX-512,则使用
vpternlogd
进行偏移和搅拌,可以避免在改组之前掩盖数据,而vpermb
而不是vpshufb
避免需要掩盖控件,因此您将避免完全完全常数set1_epi8(0x0f)
常数。Clang的Shuffle Optimizer没有发现任何内容,只需像GCC一样将其编译为撰写( https://godbolt.orgg/ z/xz7ttbm1d ),即使使用
-march = sapphirerapids
。没有发现它可以使用vpermb
而不是vpand
/vpshufb
。(没有AVX,它需要2个额外的
movdqa
寄存器 - 复制指令。)Arbitrary shuffles with a control vector that has to be stored this way? Ugh, hard to work with. I guess you'd have to unpack both to feed SSSE3
pshufb
and then re-pack that result.Probably just
punpcklbw
against a right-shifted copy, then AND mask to keep only the low 4 bits in each byte. Thenpshufb
.Sometimes an odd/even split is easier than widening each element (so bits just stay within their original byte or word). In this case, if we could change your nibble index numbering,
punpcklqdq
could put the odd or even nibbles in the high half, ready to bring them back down and OR.But without doing that, re-packing is a separate problem. I guess combine adjacent pairs of bytes into a word in the low byte, perhaps with
pmaddubsw
if throughput is more important than latency. Then you canpackuswd
(against zero or itself) orpshufb
(with a constant control vector).If you were doing multiple such shuffles, you could pack two vectors down to one, to store with
movhps
/movq
. Using AVX2, it might be possible to have all the other instructions working on two independent shuffles in the two 128-bit lanes.Masking the data with
0x0f
ahead of the shuffle (instead of after) allows more ILP on CPUs with two shuffle units. At least if they already had the uint64_t values in vector registers, or if the data and control values are coming from memory so both can be loaded in the same cycle. If coming from GPRs, 1/clock throughput forvmovq xmm, reg
means there's a resource conflict between the dep chains so they can't both start in the same cycle. But since we the data might be ready before the control, masking early keeps it off the critical path for control->output latency.If latency is a bottleneck instead of the usual throughput, consider replacing
pmaddubsw
with right-shift by 4,por
, and AND/pack. Orpshufb
to pack while ignoring garbage in odd bytes. Since you'd need another constant anyway, might as well make it apshufb
constant instead ofand
.If you had AVX-512, a shift and bit-blend with
vpternlogd
could avoid needing to mask the data before shuffling, andvpermb
instead ofvpshufb
would avoid needing to mask the control, so you'd avoid theset1_epi8(0x0f)
constant entirely.clang's shuffle optimizer didn't spot anything, just compiling it as-written like GCC does (https://godbolt.org/z/xz7TTbM1d), even with
-march=sapphirerapids
. Not spotting that it could usevpermb
instead ofvpand
/vpshufb
.(Without AVX, it requires 2 extra
movdqa
register-copy instructions.)今天我遇到了这个问题。在AVX-512中,您可以使用
vpmultishiftqb
( 8位的块。以下是实现。clang屈服( 2 ):
就我而言,我正在将64-bit-Element element element vectors中的nibbles改组为;此方法还避免了扩大的需求。如果您的洗牌是/是恒定的,并且您保持在向量,则此方法将减少到四个说明:2x
vpmultishiftqb
,1xvpslld
和1xvpternlogd
。计数µOPS表明延迟5,每2个周期的吞吐量为1个,瓶装在洗牌µOPS上,用于128位和256位载体;由于后两个说明减少了执行单元,因此3对于512位向量的吞吐量为3。I came across this problem today. In AVX-512 you can use
vpmultishiftqb
(1), an amusing instruction available in Ice Lake and after (and apparently in Zen 4, according to Wikipedia), to shuffle nibbles much more quickly. Its power lies in its ability to permute bytes in an unaligned fashion: It takes the eight 8-bit chunks in each 64-bit element and selects unaligned 8-bit chunks from the corresponding element. Below is an implementation.Clang yields (2):
In my case, I am shuffling nibbles in 64-bit-element vectors; this method also avoids the need for widening. If your shuffle(s) is/are constant and you stay in vectors, this method reduces to a measly four instructions: 2x
vpmultishiftqb
, 1xvpslld
, and 1xvpternlogd
. Counting µops suggests a latency of 5 and throughput of one every 2 cycles, bottlenecked on shuffle µops, for 128- and 256-bit vectors; and a throughput of 3 for 512-bit vectors, due to reduced execution units for the latter two instructions.