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

如何用变量实现AVX洗牌?寻求_mm_shuffle_epi32替代优化方案

优化固定查找表搜索的SIMD实现

你的核心需求是用SIMD指令替代标量循环,提升批量处理vindex的性能,以下是几种更优的处理方式,基于不同指令集兼容性:

1. 基于AVX2的并行优化(推荐,吞吐量最高)

利用AVX2的256位向量指令,一次处理8个int8_t索引,批量生成对应查找结果:

步骤说明

  • 准备查找表向量:将4个int32_t的表元素广播到256位向量中:
    __m256i table_vec = _mm256_setr_epi32(t0, t1, t2, t3, t0, t1, t2, t3);
    
  • 批量加载索引:每次加载8个int8_t索引到128位向量(高8字节可忽略):
    for (uint32_t i = 0; i < 100; i += 8) {
        __m128i idx_vec = _mm_loadu_si128((__m128i*)&vindex[i]);
    
  • 提取每个索引的4个2-bit片段:通过字节级移位和掩码操作,分离出每个索引的4组2-bit索引:
    __m128i idx0 = _mm_and_si128(idx_vec, _mm_set1_epi8(0x03));       // 低2位
        __m128i idx1 = _mm_and_si128(_mm_srli_epi8(idx_vec, 2), _mm_set1_epi8(0x03)); // 右移2位后的2位
        __m128i idx2 = _mm_and_si128(_mm_srli_epi8(idx_vec, 4), _mm_set1_epi8(0x03)); // 右移4位后的2位
        __m128i idx3 = _mm_and_si128(_mm_srli_epi8(idx_vec, 6), _mm_set1_epi8(0x03)); // 右移6位后的2位
    
  • 扩展索引并查表:将8位索引扩展为32位,用_mm256_permutevar_epi32(支持向量索引的洗牌指令,替代需要立即数的_mm_shuffle_epi32)批量查表:
    __m256i idx0_32 = _mm256_cvtepi8_epi32(idx0);
        __m256i idx1_32 = _mm256_cvtepi8_epi32(idx1);
        __m256i idx2_32 = _mm256_cvtepi8_epi32(idx2);
        __m256i idx3_32 = _mm256_cvtepi8_epi32(idx3);
    
        __m256i res0 = _mm256_permutevar_epi32(table_vec, idx0_32);
        __m256i res1 = _mm256_permutevar_epi32(table_vec, idx1_32);
        __m256i res2 = _mm256_permutevar_epi32(table_vec, idx2_32);
        __m256i res3 = _mm256_permutevar_epi32(table_vec, idx3_32);
    
  • 重新排列结果并存储:将4组结果向量交错排列,匹配pdata的连续存储格式,然后批量写入内存:
    // 交错排列结果:res0[0], res1[0], res2[0], res3[0], res0[1], ..., res3[7]
        __m256i tmp0 = _mm256_unpacklo_epi32(res0, res1);
        __m256i tmp1 = _mm256_unpacklo_epi32(res2, res3);
        __m256i tmp2 = _mm256_unpackhi_epi32(res0, res1);
        __m256i tmp3 = _mm256_unpackhi_epi32(res2, res3);
    
        __m256i final0 = _mm256_unpacklo_epi64(tmp0, tmp1);
        __m256i final1 = _mm256_unpackhi_epi64(tmp0, tmp1);
        __m256i final2 = _mm256_unpacklo_epi64(tmp2, tmp3);
        __m256i final3 = _mm256_unpackhi_epi64(tmp2, tmp3);
    
        // 批量存储到pdata
        _mm256_storeu_si256((__m256i*)&pdata[i*4], final0);
        _mm256_storeu_si256((__m256i*)&pdata[i*4 + 32], final1);
        _mm256_storeu_si256((__m256i*)&pdata[i*4 + 64], final2);
        _mm256_storeu_si256((__m256i*)&pdata[i*4 + 96], final3);
    }
    
  • 处理剩余元素:最后对不足8个的剩余索引(100%8=4个),用原标量循环或简化的SIMD代码处理。

2. 基于SSE4.1的兼容方案

如果仅支持SSE4.1指令集,用128位向量一次处理4个索引,逻辑和AVX2版本类似,核心差异是用_mm_permutevar_epi32替代256位指令,每次处理4个vindex元素,最终吞吐量略低于AVX2版本,但仍优于标量循环。

3. 字节级查表优化(适合小范围结果)

如果table的int32_t元素低8位即可满足需求(或可转换为字节级存储),可以用_mm_shuffle_epi8(SSE2指令)实现字节级批量查表:

  • 将table转换为字节向量:__m128i table_byte = _mm_setr_epi8(t0&0xFF, t0&0xFF, t0&0xFF, t0&0xFF, t1&0xFF, ..., t3&0xFF);(每个元素重复4次,对应32位的4个字节)
  • 提取索引后直接用_mm_shuffle_epi8查表,再将字节结果扩展为32位。这种方式指令延迟更低,但仅适用于结果可压缩为字节的场景。

性能对比

  • 标量循环:每次处理1个索引,生成4个32位结果,受CPU分支预测和内存访问限制,吞吐量较低。
  • SIMD优化版本:一次处理8/4个索引,并行完成查表和结果排列,吞吐量可提升3-7倍(取决于CPU和指令集),远优于原循环与_mm_shuffle_epi32相当的性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:56:03