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

如何进一步优化基于数组操作的XOR运算代码?

数组XOR运算性能优化求助

我正在对result和DB两个数组执行XOR运算,result通过偏移量rotate1进行访问。目前已采用AVX2指令集、循环展开和预取优化,但仍存在性能瓶颈:50%时间消耗在XOR操作,40%在数据存储,分支的else部分每次调用仅执行一次。现提供代码及详细实现细节,寻求进一步优化方案:

void perform_array_xor(uint32_t partindex, uint32_t offset, uint64_t *result, uint32_t EntrySize, uint32_t PartSize)
{
    auto B = 1;

    assert(EntrySize/8==B);

    // Ensure that PartSize is a multiple of 32 for this example
    if (PartSize % 8 != 0)
    {
        // Handle this case
        return;
    }

     __m256i a,b,r;

     unsigned int rotate1_1;
     int k;
    
    for (int i = 0; i < PartSize; i += 8)
    {
        rotate1_1 = (i + offset) & (PartSize - 1);


        _mm_prefetch(result + rotate1_1, _MM_HINT_T2);
        k = 0;
        if(rotate1_1 + 7 < PartSize){
            a = _mm256_loadu_si256((__m256i*)(result + rotate1_1));
            b = _mm256_loadu_si256((__m256i*)(DB + partindex + i));
            r = _mm256_xor_si256(a, b);
            _mm256_storeu_si256((__m256i*)(result + rotate1_1), r);
            //std::memcpy(result + rotate1_1, &r, sizeof(__m256i));
            
            k = 4 ;
            a = _mm256_loadu_si256((__m256i*)(result + rotate1_1 + k));
            b = _mm256_loadu_si256((__m256i*)(DB + partindex + i + k));
            r = _mm256_xor_si256(a, b);
            _mm256_storeu_si256((__m256i*)(result + rotate1_1 + k), r);
            //std::memcpy(result + rotate1_1 + k, &r, sizeof(__m256i));
            
        }
        else{
        result[(rotate1_1 + 0) & (PartSize - 1)] ^= DB[partindex + (i + 0)];
        result[(rotate1_1 + 1) & (PartSize - 1)] ^= DB[partindex + (i + 1)];
        result[(rotate1_1 + 2) & (PartSize - 1)] ^= DB[partindex + (i + 2)];
        result[(rotate1_1 + 3) & (PartSize - 1)] ^= DB[partindex + (i + 3)];
        result[(rotate1_1 + 4) & (PartSize - 1)] ^= DB[partindex + (i + 4)];
        result[(rotate1_1 + 5) & (PartSize - 1)] ^= DB[partindex + (i + 5)];
        result[(rotate1_1 + 6) & (PartSize - 1)] ^= DB[partindex + (i + 6)];
        result[(rotate1_1 + 7) & (PartSize - 1)] ^= DB[partindex + (i + 7)];
        }
        
    }
}

更新信息

  • DB数组大小为2^28,总计2GB数据
  • result数组大小为2^14,总计128KB
  • 每次函数调用时result数组保持不变
  • 每次调用会访问DB中连续的2^14个条目
  • 当前处理完整DB耗时143ms,约13GB/s
  • 目标平台为9代i7处理器,编译器为Clang

优化方案

1. 消除分支预测开销

既然else分支仅执行一次,直接把循环拆分为两部分:先处理所有不需要进入else的完整迭代,最后单独处理触发else的那一轮,彻底移除循环内的分支判断,避免分支预测失败打断CPU流水线。

2. 优化内存访问模式

  • result仅128KB,完全能被L1缓存容纳,当前的手动预取属于冗余操作,建议移除_mm_prefetch调用,让CPU硬件预取器自动处理。
  • 对DB的连续访问可以添加显式预取,比如在循环中预取DB + partindex + i + 32(提前1-2个缓存行),进一步掩盖内存读取延迟。

3. AVX2指令对齐优化

  • 将_mm256_loadu_si256/_mm256_storeu_si256替换为对齐版本_mm256_load_si256/_mm256_store_si256,前提是确保result和DB按32字节对齐。可通过编译器属性__attribute__((aligned(32)))或内存分配函数_mm_malloc实现,对齐访问能大幅提升内存读写效率。
  • 进一步展开循环,一次处理16个元素(四个256位向量),提升指令级并行度,让CPU执行单元充分利用。

4. 指令调度优化

调整指令顺序,将加载、XOR、存储操作交错排列,利用CPU乱序执行特性重叠处理不同操作,隐藏存储和计算的延迟:

// 交错执行两组向量操作
a1 = _mm256_load_si256(...);
b1 = _mm256_load_si256(...);
a2 = _mm256_load_si256(...);
b2 = _mm256_load_si256(...);
r1 = _mm256_xor_si256(a1, b1);
r2 = _mm256_xor_si256(a2, b2);
_mm256_store_si256(...);
_mm256_store_si256(...);

5. 强化编译器优化选项

确保开启以下编译选项:

-O3 -mavx2 -march=skylake -funroll-loops

显式指定Skylake架构(9代i7属于该架构),让编译器生成更针对性的优化指令;-funroll-loops辅助循环展开,提升并行度。

6. 重构循环逻辑

提前计算循环次数,将DB指针缓存到局部变量,减少内存寻址的间接开销;利用PartSize是2的幂的特性,缓存PartSize -1为局部变量,减少重复计算。

优化后示例代码

void perform_array_xor(uint32_t partindex, uint32_t offset, uint64_t *result, uint32_t EntrySize, uint32_t PartSize)
{
    const auto B = 1;
    assert(EntrySize / 8 == B);
    assert(PartSize % 8 == 0); // 提前断言,移除运行时判断

    __m256i a1, b1, r1, a2, b2, r2;
    const uint64_t* db_ptr = DB + partindex;
    const uint32_t part_size_mask = PartSize - 1;
    const uint32_t full_loop_count = (PartSize / 8) - 1;

    // 处理所有完整迭代,无分支
    for (int i = 0; i < full_loop_count; ++i) {
        const uint32_t rotate1_1 = (offset + i * 8) & part_size_mask;
        
        // 交错加载,提升指令并行度
        a1 = _mm256_load_si256((__m256i*)(result + rotate1_1));
        b1 = _mm256_load_si256((__m256i*)(db_ptr + i * 8));
        a2 = _mm256_load_si256((__m256i*)(result + rotate1_1 + 4));
        b2 = _mm256_load_si256((__m256i*)(db_ptr + i * 8 + 4));
        
        // XOR运算
        r1 = _mm256_xor_si256(a1, b1);
        r2 = _mm256_xor_si256(a2, b2);
        
        // 存储结果
        _mm256_store_si256((__m256i*)(result + rotate1_1), r1);
        _mm256_store_si256((__m256i*)(result + rotate1_1 + 4), r2);
    }

    // 单独处理最后一轮,避免循环内分支
    const int last_i = full_loop_count * 8;
    const uint32_t rotate1_1 = (offset + last_i) & part_size_mask;
    if (rotate1_1 + 7 < PartSize) {
        a1 = _mm256_load_si256((__m256i*)(result + rotate1_1));
        b1 = _mm256_load_si256((__m256i*)(db_ptr + last_i));
        r1 = _mm256_xor_si256(a1, b1);
        _mm256_store_si256((__m256i*)(result + rotate1_1), r1);

        a2 = _mm256_load_si256((__m256i*)(result + rotate1_1 + 4));
        b2 = _mm256_load_si256((__m256i*)(db_ptr + last_i + 4));
        r2 = _mm256_xor_si256(a2, b2);
        _mm256_store_si256((__m256i*)(result + rotate1_1 + 4), r2);
    } else {
        result[(rotate1_1 + 0) & part_size_mask] ^= db_ptr[last_i + 0];
        result[(rotate1_1 + 1) & part_size_mask] ^= db_ptr[last_i + 1];
        result[(rotate1_1 + 2) & part_size_mask] ^= db_ptr[last_i + 2];
        result[(rotate1_1 + 3) & part_size_mask] ^= db_ptr[last_i + 3];
        result[(rotate1_1 + 4) & part_size_mask] ^= db_ptr[last_i + 4];
        result[(rotate1_1 + 5) & part_size_mask] ^= db_ptr[last_i + 5];
        result[(rotate1_1 + 6) & part_size_mask] ^= db_ptr[last_i + 6];
        result[(rotate1_1 + 7) & part_size_mask] ^= db_ptr[last_i + 7];
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:24:54