sse - 将 32 位 int 中打包的 8 个 4 位值零扩展到 __m256i 的英特尔矢量指令?

标签 sse avx avx2

如问题所述,我有一个正常的 int,它是 8 个打包值,每个值 4 位,我想将其零扩展到 256 位向量寄存器中。 sse/avx/avx2 可以吗?

最佳答案

solution by chtz (其余部分称为cvt_nib_epi32_chtz)非常合适 用于一般用途。但是,在某些特定情况下,下面介绍的解决方案可能 稍微更有效率:

/*     gcc -O3 -m64 -Wall -march=skylake cvt_nib_epi32.c     */
#include <immintrin.h>
#include <stdio.h>
#include <stdint.h>

__m256i cvt_nib_epi32_SKL(uint32_t x) {                /* Efficient on Intel Skylake and newer         */
                                                       /* Broadcast x to 8 elements                    */
    __m256i input   = _mm256_set1_epi32(x);            
                                                       /* Shift the nibbles to the right position      */
    __m256i shifted = _mm256_srlv_epi32(input,_mm256_set_epi32(28,24,20,16,12,8,4,0)); 
                                                       /* Mask off the unwanted bits and return        */
            return _mm256_and_si256(shifted, _mm256_set1_epi32(0xF)); 
}


__m256i cvt_nib_epi32_HSW(uint32_t x) {                /* Efficient on intel Haswell and Broadwell     */
                                                       /* Very inefficient in AMD Zen!                 */
    __uint64_t x_b = _pdep_u64(x, 0x0F0F0F0F0F0F0F0F); /* Expand nibbles to bytes                      */
    __m128i    x_v = _mm_cvtsi64_si128(x_b);           /* Move x_b from GPR to AVX vector register     */
    return _mm256_cvtepu8_epi32(x_v);                  /* Convert bytes to integer elements and return */
} 

下面的程序集是generated by gcc :

cvt_nib_epi32_SKL:
        vmovd   xmm0, edi
        vpbroadcastd    ymm0, xmm0
        vpsrlvd ymm0, ymm0, YMMWORD PTR .LC0[rip]
        vpand   ymm0, ymm0, YMMWORD PTR .LC1[rip]
        ret
cvt_nib_epi32_HSW:
        movabs  rax, 1085102592571150095
        mov     edi, edi
        pdep    rdi, rdi, rax
        vmovq   xmm0, rdi
        vpmovzxbd       ymm0, xmm0
        ret
cvt_nib_epi32_chtz:
        vmovd   xmm0, edi
        vpsrld  xmm1, xmm0, 4
        vpunpcklbw      xmm0, xmm0, xmm1
        vpand   xmm0, xmm0, XMMWORD PTR .LC2[rip]
        vpmovzxbd       ymm0, xmm0
        ret

函数cvt_nib_epi32_chtz非常适合AMD zen微架构, 因为它不使用指令 pdepvpsrlvd,它们在这些处理器上很慢。

在 Intel 处理器上,cvt_nib_epi32_chtz 可能会受到影响 来自高端口 5 (p5) 的压力,取决于周围的代码, 因为 vmovdvpunpcklbwvpmovzxbd 都在 p5 上执行。 其他函数仅解码为 2 p5 微指令。

Skylake 解决方案 cvt_nib_epi32_SKL 使用了 vpsrlvd,速度较慢 在 Intel Haswell 和 Broadwell 上。 对于这些处理器,cvt_nib_epi32_HSW 是合适的。它使用 BMI2 指令 pdep,在 AMD 禅宗微架构。请注意,cvt_nib_epi32_HSW 也应该在 Intel Skylake 上运行良好,但是 (再次)实际性能取决于周围的代码。

请注意,在循环上下文中不断加载,例如 YMMWORD PTR .LC0[rip]movabs rax, 1085102592571150095, 很可能被提升到循环之外。在这种情况下,只需要 4 微指令 cvt_nib_epi32_HSWcvt_nib_epi32_SKL

关于sse - 将 32 位 int 中打包的 8 个 4 位值零扩展到 __m256i 的英特尔矢量指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55084047/

相关文章:

c - 在 `static const` 函数中定义 `C` SIMD 变量

performance - 简单阵列处理循环的 AVX 512 与 AVX2 性能对比

c - 如何在 SSE 中有效地结合比较?

intel - 在 AVX/SSE 中将寄存器与任意 channel 选择结合起来的最快/最好的方法是什么?

c++ - 内在代码优化提示

c++ - 可以原子地获取和运算的最大数据类型?

assembly - 如何统计avx和avx2指令集的数量

c++ - AVX2 列总体计数算法分别针对每个位列

opencv - OpenCV 中的 Mat 矩阵和 SSE 的 16 字节对齐

python - 如何检查我安装的 numpy 是否使用 SSE/SSE2 指令集编译?