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

AVX2是否存在将16位值广播加载16次至__m256i寄存器的指令及内在函数?

回答:AVX2实现16位值广播到__m256i寄存器

当然有!AVX2正好提供了专门的内在函数来完成你需要的**将单个16位值广播填充到__m256i寄存器的所有16位分量(共16次)**的操作,完全可以替代你代码里的loadLow16函数。

核心解决方案:_mm256_set1_epi16内在函数

AVX2的_mm256_set1_epi16(uint16_t a)内在函数会接收一个16位无符号整数,然后将其复制到__m256i寄存器的每一个16位元素中(256位寄存器恰好包含16个16位元素)。对应的硬件指令是vpbroadcastw,只需要单条指令就能完成操作,效率非常高。

修正你的代码

先指出你代码里的几个小问题,再给出完整的修正版本:

  1. 你的masks数组最后一个元素写错了:应该是1<<15(16位无符号数的最高位是第15位,从0开始计数),而不是1<<16。
  2. 循环里的vals是笔误,应该替换为对应的v1/v2/v3/v4。
  3. 用_mm256_set1_epi16替换loadLow16。

修正后的完整代码:

#include <immintrin.h>
#include <stdint.h>

void countHistBits4(const uint64_t p[], uint32_t n, uint32_t hist[64]) {
    // 将mask数组设为静态常量并32字节对齐,优化加载效率
    static const uint16_t masks[16] __attribute__((aligned(32))) = {
        1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
        1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15
    };
    __m256i mask = _mm256_load_si256((const __m256i*)masks);
    __m256i count1 = _mm256_setzero_si256();
    __m256i count2 = _mm256_setzero_si256();
    __m256i count3 = _mm256_setzero_si256();
    __m256i count4 = _mm256_setzero_si256();

    for (uint32_t i = 0; i < n; i++) {
        uint64_t val = p[i];
        // 用_mm256_set1_epi16实现16位值的广播加载
        __m256i v1 = _mm256_set1_epi16((uint16_t)(val & 0xFFFF));
        __m256i v2 = _mm256_set1_epi16((uint16_t)((val >> 16) & 0xFFFF));
        __m256i v3 = _mm256_set1_epi16((uint16_t)((val >> 32) & 0xFFFF));
        __m256i v4 = _mm256_set1_epi16((uint16_t)((val >> 48) & 0xFFFF));

        // 按位与mask后累加统计
        v1 = _mm256_and_si256(v1, mask);
        count1 = _mm256_adds_epi16(count1, v1);
        
        v2 = _mm256_and_si256(v2, mask);
        count2 = _mm256_adds_epi16(count2, v2);
        
        v3 = _mm256_and_si256(v3, mask);
        count3 = _mm256_adds_epi16(count3, v3);
        
        v4 = _mm256_and_si256(v4, mask);
        count4 = _mm256_adds_epi16(count4, v4);
    }

    // 将寄存器中的统计结果导出到hist数组
    uint16_t tmp[16];
    _mm256_storeu_si256((__m256i*)tmp, count1);
    for (int j = 0; j < 16; j++) hist[j] += tmp[j];
    
    _mm256_storeu_si256((__m256i*)tmp, count2);
    for (int j = 0; j < 16; j++) hist[j+16] += tmp[j];
    
    _mm256_storeu_si256((__m256i*)tmp, count3);
    for (int j = 0; j < 16; j++) hist[j+32] += tmp[j];
    
    _mm256_storeu_si256((__m256i*)tmp, count4);
    for (int j = 0; j < 16; j++) hist[j+48] += tmp[j];
}

额外优化建议

  • 把masks数组声明为static const并32字节对齐,这样编译器可以把它放到只读数据段,_mm256_load_si256加载时能利用对齐内存的带宽优势。
  • 你使用的_mm256_adds_epi16是饱和加法,适合统计次数不超过65535的场景。如果你的n非常大,可能需要换成普通加法(_mm256_add_epi16)并在最后处理溢出,或者用32位累加寄存器来避免饱和限制。
  • 部分编译器也支持_mm256_broadcastw_epi16,它和_mm256_set1_epi16功能一致,编译器会自动选择最优的指令实现,你可以根据习惯选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 19:32:34