You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVX512:如何将32个16位字按顺序拆分为两组32位双字并打包?

解决方案:AVX512拆分16位字寄存器为32位双字寄存器

原代码中使用_mm512_unpacklo/hi_epi16无法满足需求的核心原因是:这类unpack指令在每个128位lane内部对16位元素进行交错拆分,而非按整个寄存器的前16/后16元素分割,因此得到的是分组交错的结果,不符合你的要求。

以下是两种高效且直观的实现方案:

方案1:拆分+扩展(推荐,逻辑清晰且性能最优)

先将原__m512i寄存器拆分为两个__m256i(各包含16个16位字),再分别将16位字零扩展为32位双字:

#include <immintrin.h>
#include <stdio.h>

int main()
{
    unsigned short int Src[32], Dst[32];
    int DstHi[16], DstLo[16];

    for (int i = 0; i < 32; i++) Src[i] = i;

    __m512i src = _mm512_loadu_si512((__m512i*)Src);

    // 拆分原寄存器:取前256位(16个16位字)和后256位(16个16位字)
    __m256i src_lo_256 = _mm512_extracti64x4_epi64(src, 0);
    __m256i src_hi_256 = _mm512_extracti64x4_epi64(src, 1);

    // 将无符号16位字零扩展为32位双字,填充到__m512i寄存器
    __m512i dst_lo = _mm512_cvtepu16_epi32(src_lo_256);
    __m512i dst_hi = _mm512_cvtepu16_epi32(src_hi_256);

    // 验证输出
    _mm512_storeu_si512((__m512i*)DstLo, dst_lo);
    _mm512_storeu_si512((__m512i*)DstHi, dst_hi);

    printf("DstLo (前16个16位字扩展为32位):");
    for (int i = 0; i < 16; i++) printf("%d,", DstLo[i]);
    printf("\nDstHi (后16个16位字扩展为32位):");
    for (int i = 0; i < 16; i++) printf("%d,", DstHi[i]);

    // 可选:将32位双字压缩回16位字,验证数据完整性
    __m512i dst = _mm512_packus_epi32(dst_lo, dst_hi);
    _mm512_storeu_si512((__m512i*)Dst, dst);
    printf("\nDst packed back:\n");
    for (int i = 0; i < 32; i++) printf("%d;", Dst[i]);

    return 0;
}

关键指令说明:

  • _mm512_extracti64x4_epi64:将__m512i按64位块拆分,参数0提取前4个64位块(共256位,对应原寄存器的前16个16位字),参数1提取后4个64位块。
  • _mm512_cvtepu16_epi32:将__m256i中的16个无符号16位元素零扩展为16个32位元素,刚好填满一个__m512i寄存器,高位自动补0。

方案2:掩码重排(紧凑实现)

如果希望用更少的指令完成操作,可以利用_mm512_permutexvar_epi32配合预定义掩码直接重排元素:

#include <immintrin.h>
#include <stdio.h>

int main()
{
    unsigned short int Src[32], Dst[32];
    int DstHi[16], DstLo[16];

    for (int i = 0; i < 32; i++) Src[i] = i;

    __m512i src = _mm512_loadu_si512((__m512i*)Src);

    // 预定义重排掩码:分别对应前16和后16个16位元素的扩展位置
    const __m512i mask_lo = _mm512_set_epi32(7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8);
    const __m512i mask_hi = _mm512_set_epi32(23,22,21,20,19,18,17,16,31,30,29,28,27,26,25,24);

    // 将整个__m512i的32个16位元素零扩展为64个32位元素,存入两个__m512i
    __m512i src_epi32_lo = _mm512_cvtepu16_epi32(_mm512_castsi512_si256(src));
    __m512i src_epi32_hi = _mm512_cvtepu16_epi32(_mm512_extracti64x4_epi64(src, 1));

    // 合并扩展后的元素
    __m512i src_epi32 = _mm512_inserti64x4_epi64(src_epi32_lo, src_epi32_hi, 1);

    // 按掩码重排得到目标寄存器
    __m512i dst_lo = _mm512_permutexvar_epi32(mask_lo, src_epi32);
    __m512i dst_hi = _mm512_permutexvar_epi32(mask_hi, src_epi32);

    // 验证输出
    _mm512_storeu_si512((__m512i*)DstLo, dst_lo);
    _mm512_storeu_si512((__m512i*)DstHi, dst_hi);

    printf("DstLo (前16个16位字扩展为32位):");
    for (int i = 0; i < 16; i++) printf("%d,", DstLo[i]);
    printf("\nDstHi (后16个16位字扩展为32位):");
    for (int i = 0; i < 16; i++) printf("%d,", DstHi[i]);

    return 0;
}

说明:

  • 先将原寄存器的前后两部分分别扩展为32位,再合并为一个1024位的虚拟向量(通过两个__m512i拼接),最后用掩码重排提取需要的元素。
  • 该方案指令数略少,但逻辑相对复杂,性能与方案1接近,适合对代码紧凑性有要求的场景。

内容的提问来源于stack exchange,提问作者Andrey Dmitriev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 07:18:04