_mm256_packs_epi32,除了按顺序打包

_mm256_packs_epi32, except pack sequentially

可以使用_mm256_packs_epi32。如下:__m256i e = _mm256_packs_epi32 ( ai, bi);

在调试器中,我看到 ai 的值:m256i_i32 = {0, 1, 0, 1, 1, 1, 0, 1}。我还看到了 bi 的值:m256i_i32 = {1, 1, 1, 1, 0, 0, 0, 1}。包装给了我e:m256i_i16 = {0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1}。包装是交错的。所以我们在 e 中有 ai 中的前四个数字,bi 中的前四个数字,ai 中的最后四个数字,bi 中的最后四个数字。

我想知道是否有一条指令可以将 ai 和 bi 并排打包而无需交错。

打包后的 vpermq 可以工作,但我想知道是否有一条指令可以实现这一点。

不幸的是,在 AVX-512 之前没有顺序跨通道包。 (即使这样也只针对 1 个寄存器,或者不饱和。)

vpacksswdvpalignr 这样的 shuffle 的通道内行为是 AVX2 的主要缺点之一,它使得这些 shuffle 的 256 位版本不如它们的有用 __m128i 版本。但是在 Intel 和 Zen2 CPU 上,如果您需要特定顺序的元素,通常最好还是使用 __m256i 向量,最后使用 vpermq 。 (或者 vpermd 在 2 层打包后带有向量常数:


如果您的 32 位元素来自解包较窄的元素,并且您不关心较宽元素的顺序,您可以使用通道内解包来加宽,这让您可以打包回到原来的顺序。

这对于零扩展解包来说很便宜:_mm256_unpacklo/hi_epi16_mm256_setzero_si256())。这与 vpmovzxwd (_mm256_cvtepu16_epi32) 一样便宜,而且实际上更好,因为您可以对源数据进行 256 位加载并以两种方式解压缩,而不是通过窄加载来馈送 vpmovzx...仅适用于输入寄存器底部的数据。 (并且内存源 vpmovzx... ymm, [mem] 无法将负载与 YMM 目标微融合,仅适用于 128 位 XMM 版本,在 Intel CPU 上,因此前端成本与单独加载和随机播放相同说明。)

但是对于需要符号扩展的数据,这个技巧并不适用。 vpcmpgtw 获得 vpunpckl/hwd 的高半部分确实有效,但是 vpermq 重新打包时效果差不多,只是执行端口压力不同。所以vpmovsxwd那里更简单。


将数据分割成 odd/even 而不是 low/high 也可以, 例如将 16 位元素零扩展为 32 位元素:

   auto  veven  = _mm256_and_si256(v, _mm256_set1_epi32(0x0000FFFF));
   auto  vodd   = _mm256_srli_epi32(v, 16);

经过处理后,可以通过shift和vpblendw重新组合。 (Intel Skylake / Ice Lake 上的端口 5 为 1 uop)。或者对于字节,vpblendvb 带有一个控制向量,但是在 Intel CPU 上花费 2 微指令(但对于任何端口),而在 Zen2 上只需要 1 微指令。 (这些 uop 计数不包括 vpslld ymm, ymm, 16 移位以将奇数元素与其起点对齐。)


即使使用 AVX-512,情况也不尽如人意。您仍然可以使用单个 shuffle uop 将 2 个向量组合为相同宽度的向量之一。

对于任何一对元素大小,如 vpmovzx / sx 的倒数,具有 t运行 阳离子或有符号或无符号饱和度的非常好的单向量变窄。例如qword to byte vpmov[su]qb,带有可选的内存目标。

(有趣的事实:vpmovdm [rdi]{k1}, zmm0 是 Xeon Phi(缺少 AVX-512BW 和 AVX-512VL)可以对内存进行字节掩码存储的唯一方法;这可能就是这些存在于内存目标中的原因形式。在像 Skylake-X / Ice Lake 这样的主流 Intel 上,内存目标版本并不比单独打包到寄存器然后存储便宜。https://uops.info/)

AVX-512 也有很好的带有控制向量的 2 输入混洗,所以对于 dword-to-word t运行cation 你可以使用 vpermt2w zmm1, zmm2, zmm3。但这需要一个洗牌控制向量,vpermt2w 在 SKX 和 IceLake 上是 3 微码。 (t2dt2q 是 1 uop)。 vpermt2b 仅在 AVX-512VBMI (Ice Lake) 中可用,并且在那里也是 3 uops。

(与 Ice Lake 上的 1 uop vpermbstill 2 uops on Ice Lake 的 AVX-512BW vpermw 不同。所以他们没有降低前端成本向后兼容的指令,但 ICL 可以 运行 端口 0 或 1 上的 2 个微指令中的 1 个,而不是端口 5 上的洗牌单元上的两个微指令。也许 ICL 有一个微指令将洗牌控制预处理为 vpermb 控制或其他东西,这也可以解释改进的延迟:数据->数据为 3 个周期,控制->数据为 4 个周期,而 2p5 微指令在 SKX 上为 6 个周期,显然是从控制和数据向量开始的串行依赖。)