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

标签 simd avx2

我有来自 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寄存器会更好吗?

最佳答案

打开 _mm256_loadu_si256 的高车道会花费更多的洗牌成本。 。大多数 AVX2 CPU 的负载吞吐量高于随机播放吞吐量,并且每个输出向量至少需要 1 次随机播放,因此您已经可以对 2 epi32 执行 1 次加载和 4 次随机播放。向量是一个糟糕的权衡。

如果有什么的话,最好使用 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 随机播放或您正在使用的符号扩展版本相同,vpmovsxwd = epi16_epi32)。因此,如果您关心 Zen CPU,尤其是 Zen 2,您确实希望编译器能够正确执行此操作。

关于SIMD (AVX2) - 将 uint8_t 值加载到多个 float __m256 寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66705841/

相关文章:

intrinsics - _mm256_xor_si256() 和 _mm256_xor_ps() 的区别

c - _mm256_slli_si256 : error "last argument must be an 8-bit intermediate"

c++ - avx浮点按位逻辑运算的原因是什么?

assembly - x86-64 将 long 转换为 double

c++ - g++ -O2 错误地优化了 SIMD 变量赋值

c++ - AVX(2) 收集指令如何实际计算获取地址?

performance - 点乘积性能与 SSE 指令

c++ - 使用 SSE 的矩阵乘法

assembly - Intel 的 SIMD 指令会影响奇偶校验标志吗?

rust - 为什么存储到 AVX2 256 位向量和从中加载在调试和 Release模式下有不同的结果?