SIMD (AVX2) - 将 uint8_t 值加载到多个浮点 __m256 寄存器

Posted

技术标签:

【中文标题】SIMD (AVX2) - 将 uint8_t 值加载到多个浮点 __m256 寄存器【英文标题】:SIMD (AVX2) - load uint8_t values to multiple float __m256 registers 【发布时间】:2021-03-19 09:46:35 【问题描述】:

我有来自uint8_t 值的灰度图像。我想将数据加载到 SIMD。我加载了 16 个值并将它们转换为两个 __m256 浮点寄存器。

我用:

uint8_t * data = .....
size_t index = ....

//load 16 uint8_t (16 * 8 = 128bit)
__m128i val = _mm_loadu_si128((const __m128i*)(data + index));

//convert to 16bit int
__m128i lo16 = _mm_unpacklo_epi8(val, _mm_setzero_si128());
__m128i hi16 = _mm_unpackhi_epi8(val, _mm_setzero_si128());

//convert to 32bit int
__m256i lo32 = _mm256_cvtepi16_epi32(lo16);
__m256i hi32 = _mm256_cvtepi16_epi32(hi16);

//convert to float
__m256 lo = _mm256_cvtepi32_ps(lo32);
__m256 hi = _mm256_cvtepi32_ps(hi32);

有没有比我的解决方案更好的方法(没有 AVX-512)?

加载_mm256_loadu_si256 并将其“拆分”到4 个__m256 寄存器会更好吗?

【问题讨论】:

【参考方案1】:

打开_mm256_loadu_si256 的高通道将花费更多的洗牌。大多数 AVX2 CPU 的负载吞吐量高于 shuffle 吞吐量,并且您已经需要每个输出向量至少 1 次 shuffle,因此您对 2 个 epi32 向量执行 1 次加载和 4 次 shuffle 的方式已经是一个糟糕的权衡。

如果有的话,最好使用 2x _mm256_cvtepu8_epi32 来获得 cvtepi32_ps 的两个输入向量,每次随机播放一个负载。

使用内存源pmovz/sx 有点痛苦,因为您需要告诉编译器您正在对__m128i 进行窄加载(为了安全起见),并且一些编译器不会优化将零扩展加载到vpmovzx 的内存源中。 见Loading 8 chars from memory into an __m256 variable as packed single precision floats

但显然,自从我在 2015 年最初写下这个答案以来,情况已经有所改善; GCC9 及更高版本修复了错过优化的错误,现在将_mm_loadl_epi64( (const __m128i*)p) 折叠到vpmovzx 的内存源中。 clang 和 ICC 都很好,即使是旧版本。 MSVC 仍然使用单独的vmovq 进行糟糕的代码生成,即使使用-march:AVX2,甚至是 v19.28 和“最新”。 (Godbolt)。


在 Intel CPU 上 vpmovzxbd ymm, qword [mem] 始终为 2 uop;无法对负载进行微融合(仅适用于 xmm 目标)https://uops.info/table.html,因此即使编译器确实设法将 64 位内存源折叠为 mem 操作数,您也不会获得任何东西(代码大小除外)而不是使用vmovq 加载。

但在 Zen2 上,该指令的吞吐量为 2/时钟吞吐量,而 vpmovzxbd ymm, xmm 的吞吐量低于 1/时钟吞吐量(wd shuffle 或您正在使用的符号扩展版本 vpmovsxwd 相同= Epi16_epi32)。因此,如果您关心 Zen CPU,尤其是 Zen 2,您确实希望编译器能够正确处理此问题。

【讨论】:

以上是关于SIMD (AVX2) - 将 uint8_t 值加载到多个浮点 __m256 寄存器的主要内容,如果未能解决你的问题,请参考以下文章

将最佳 uint8_t 位图转换为 8 x 32 位 SIMD“布尔”向量

使用 AVX2 计算 8 个长整数的最小值

在同一个 SIMD 寄存器中串行添加值

Intel的AVX2指令集解读

你能调试自动矢量化循环吗?

是否所有支持 AVX2 的 CPU 也支持 SSE4.2 和 AVX?