【问题标题】:What is the inverse of "_mm256_cvtepi16_epi32"“_mm256_cvtepi16_epi32”的倒数是什么
【发布时间】:2018-09-18 04:40:24
【问题描述】:

我想要一个 AVX2(或更早版本)内在函数,它将 8 宽 32 位整数向量(总共 256 位)转换为 8 位宽 16 位整数向量(总共 128 位)[丢弃高 16 位每个元素]。 这应该是“_mm256_cvtepi16_epi32”的倒数。 如果没有直接指令,我应该如何最好地使用一系列指令?

【问题讨论】:

  • 您可能希望 _mm256_packs_epi32 使用原始向量和与输入相同的混洗版本,然后提取结果压缩向量的一半。

标签: x86 g++ intrinsics avx avx2


【解决方案1】:

在 AVX512F 之前没有单指令逆。 __m128i _mm256_cvtepi32_epi16(__m256i a) (VPMOVDW),也可用于 512->256 或 128->low_half_of_128。 (输入小于 512 位 ZMM 寄存器的版本也需要 AVX512VL,因此只有 Skylake-X,而不是 Xeon Phi KNL)。

该 AVX512 指令有带符号/无符号饱和版本,但只有 AVX512 具有截断(丢弃每个元素的高字节)而不是饱和的打包指令。

或者使用 AVX512BW,您可以使用 vpermi2w 模拟车道交叉 2 输入包,以从两个 512 位输入向量生成 512 位结果。在 Skylake-AVX512 上,它解码为多个 shuffle uop,但VPMOVDW 也是如此,这也是一个粒度小于 dword(32 位)的车道交叉 shuffle。 http://instlatx64.atw.hu/ 有一个 SKX 微指令/端口的电子表格。


_mm256_packus_epi32 (vpackusdw) 这样的 SSE2/AVX2 包指令会执行有符号或无符号饱和,以及在每个 128 位通道内运行。这与vpmovzxwd 的车道交叉行为不同。

不过,您可以_mm256_and_si256 在打包前清除高字节。如果您有多个输入向量,那可能会很好,因为packs_epi32 接受 2 个输入向量并产生 256 位输出。

a = H G F E | D C B A    32-bit signed elements, shown from high element to low element, low 128-bit lane on the right
b = P O N M | L K J I

_mm256_packus_epi32(a, b)   16-bit unsigned elements
    P O N M H G F E  |  L K J I D C B A
      elements from first operand go to the low half of each lane

如果您可以有效地使用 2x vpand / vpackuswd ymm / vpermq ymm 来获得所有元素按正确顺序排列的 256 位向量,那么这可能是 Intel CPU 上的最佳选择。每 256 位结果只有 2 个 shuffle uops(总共 4 个 uops),您可以将它们放在一个向量中。


您可以使用 SSSE3 / AVX2 vpshufb (_mm256_shuffle_epi8) 从单个输入中提取所需的字节,并将每个 128 位通道的另一半归零(通过设置该元素的随机控制值以设置符号位)。然后使用 AVX2 vpermq 将两个通道中的数据混洗到低位 128 中。

__m256i trunc_elements = _mm256_shuffle_epi8(res256, shuffle_mask_32_to_16);
__m256i ordered = _mm256_permute4x64_epi64(trunc_elements, 0x58);
__m128i result  = _mm256_castsi256_si128(ordered);   // no asm instructions

所以这是每 128 位结果 2 个微指令,但是这两个微指令都是仅在支持 AVX2 的主流英特尔 CPU 上的端口 5 上运行的洗牌。作为循环的一部分,这很好,它可以做大量的工作来保持 port0 / port1 忙碌,或者如果你需要单独的每个 128 位块。


对于 Ryzen/Excavator,穿越车道 vpermq 很昂贵(因为它们将 256 位指令拆分为多个 128 位微指令,并且没有真正的车道交叉洗牌单元:http://agner.org/optimize/)。所以你想vextracti128 / vpor 合并。或者可能是vpunpcklqdq,这样您就可以使用set1_epi64 加载相同的随机掩码,而不需要一个完整的 256 位向量常量来将上通道中的元素随机播放到该通道的上 64 位。

【讨论】:

  • 我采纳了你的最后一个建议并这样做了: _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_shuffle_epi8( res256.m, shuffle_mask.m), 0x58));带有适当定义的“shuffle_mask”。
  • @SteveBurns: 是的,如果你一次只有一个向量要做,并且不能有效地使用 2x vpand / vpackuswd / vpermq 来获得一个 256 位的向量。对于 Ryzen,你会想要 vextracti128 / vpor,因为在 Ryzen 上穿越车道 vpermq 很昂贵(它将 256 位操作分成 128 个)。
猜你喜欢
  • 1970-01-01
  • 2016-09-16
  • 2020-11-21
  • 2014-02-20
  • 1970-01-01
  • 2022-10-04
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多