_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 个寄存器,或者不饱和。)
像 vpacksswd
和 vpalignr
这样的 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 微码。 (t2d
和 t2q
是 1 uop)。 vpermt2b
仅在 AVX-512VBMI (Ice Lake) 中可用,并且在那里也是 3 uops。
(与 Ice Lake 上的 1 uop vpermb
和 still 2 uops on Ice Lake 的 AVX-512BW vpermw
不同。所以他们没有降低前端成本向后兼容的指令,但 ICL 可以 运行 端口 0 或 1 上的 2 个微指令中的 1 个,而不是端口 5 上的洗牌单元上的两个微指令。也许 ICL 有一个微指令将洗牌控制预处理为 vpermb 控制或其他东西,这也可以解释改进的延迟:数据->数据为 3 个周期,控制->数据为 4 个周期,而 2p5 微指令在 SKX 上为 6 个周期,显然是从控制和数据向量开始的串行依赖。)
可以使用_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 个寄存器,或者不饱和。)
像 vpacksswd
和 vpalignr
这样的 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 微码。 (t2d
和 t2q
是 1 uop)。 vpermt2b
仅在 AVX-512VBMI (Ice Lake) 中可用,并且在那里也是 3 uops。
(与 Ice Lake 上的 1 uop vpermb
和 still 2 uops on Ice Lake 的 AVX-512BW vpermw
不同。所以他们没有降低前端成本向后兼容的指令,但 ICL 可以 运行 端口 0 或 1 上的 2 个微指令中的 1 个,而不是端口 5 上的洗牌单元上的两个微指令。也许 ICL 有一个微指令将洗牌控制预处理为 vpermb 控制或其他东西,这也可以解释改进的延迟:数据->数据为 3 个周期,控制->数据为 4 个周期,而 2p5 微指令在 SKX 上为 6 个周期,显然是从控制和数据向量开始的串行依赖。)