将 16 位值的 __m256i 打包(饱和)到 8 位值的 __m128i?
Posted
技术标签:
【中文标题】将 16 位值的 __m256i 打包(饱和)到 8 位值的 __m128i?【英文标题】:Pack (with saturation) __m256i of 16-bit values to __m128i of 8-bit values? 【发布时间】:2021-04-25 14:53:27 【问题描述】:是否有 AVX 或 AVX2 操作可将 16x16 位无符号整数 (uint16_t) 值的 __m256i
转换为 16x8 位无符号整数 (uint8_t) 值的 __m128i
(采用饱和的低字节强>)?
有_mm256_packus_epi16()
,但它使用第一个输入的前 8 个字节,然后是第二个输入的前 8 个字节,然后是第一个和第二个输入的第二个 8 个字节......导致 8 个字节的组无序。
还有一些 AVX512 操作似乎可以满足需要,但我不能依赖 AVX512,它在许多目标机器上都不存在...
【问题讨论】:
_mm256_permute4x64_epi64
或其他改组操作之后可以修复订单吗?
我想你的意思是__m256i
。 __m256
是一个单精度浮点向量。
【参考方案1】:
不,您不能在 AVX/AVX2 的单个指令中执行此操作。
有 _mm256_packus_epi16() 但它使用来自第一个输入的前 8 个字节,然后来自第二个输入的前 8 个字节,然后来自第一个和第二个输入的第二个 8 个字节......导致 8 个字节的组无序。
以下是正确排列的方法 (AVX2):
static inline __m128i convert(__m256i data)
__m128i lo_lane = _mm256_castsi256_si128(data);
__m128i hi_lane = _mm256_extracti128_si256(data, 1);
return _mm_packus_epi16(lo_lane, hi_lane);
根据 Skylake 上的uops.info _mm256_extracti128_si256
在 p5 上是 1 µop,_mm_packus_epi16
在 p5 上是 1 µop。这意味着该代码块的吞吐量应该是 2 个周期(每两个周期进行一次转换)。
您可以使用 _mm256_extractf128_si256
定位 AVX。跨域可能会产生额外的延迟(但吞吐量应该与 AFAIK 相同)。
【讨论】:
太棒了!!谢谢! @AlexanderNovikov:如果你有很多数据要以这种方式处理,通常最好在 2 个输入上使用_mm256_packus_epi16
,然后使用 _mm256_permute4x64_epi64
来修复由 AVX2 在-车道包。一个 32 字节向量的 2 次 shuffle 显然比你从中得到的两个 16 字节向量的 4 次 shuffle 好(AMD Zen1 除外,vextracti128
非常便宜,而跨车道的 256 位 shuffle 很昂贵)
我在Fastest way to downcast an array short to char 上的回答显示了如何在一对__m256i
16x 16 位上使用pack + vpermq 修复以正确的顺序获得一个__m256i
32x 8 位。跨度>
以上是关于将 16 位值的 __m256i 打包(饱和)到 8 位值的 __m128i?的主要内容,如果未能解决你的问题,请参考以下文章
将__m256i的前N位或后N位设置为1,其余为0的有效方法