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

使用AVX实现的C++整数向量点积未达预期提速效果

整数向量点积的AVX优化问题

已实现的代码

普通循环版本

int dotProductNoAVX(const std::vector<int>& vec1, const std::vector<int>& vec2)
{
    // 用普通循环计算点积
    int dotProduct = 0;
    for (std::size_t i = 0; i < vec1.size(); ++i)
    {
        dotProduct += vec1[i] * vec2[i];
    }

    return dotProduct;
}

AVX256实现版本

// 用AVX intrinsics计算向量点积
int dotProductAVX(const std::vector<int>& vec1, const std::vector<int>& vec2)
{
    // 确保处理的元素数量是8的倍数(AVX寄存器大小)
    std::size_t size = vec1.size();
    std::size_t avxSize = size / 8 * 8;

    // 初始化AVX求和寄存器
    __m256i sum = _mm256_setzero_si256();

    // AVX并行计算点积
    for (std::size_t i = 0; i < avxSize; i += 8)
    {
        __m256i vec1_part = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&vec1[i]));
        __m256i vec2_part = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&vec2[i]));
        sum = _mm256_add_epi32(sum, _mm256_mullo_epi32(vec1_part, vec2_part));
    }

    // 将AVX寄存器结果存入数组,再累加所有元素
    int result[8];
    _mm256_storeu_si256(reinterpret_cast<__m256i*>(result), sum);
    int dotProduct = result[0] + result[1] + result[2] + result[3] + result[4] + result[5] + result[6] + result[7];

    // 处理剩余的非8倍数元素
    for (std::size_t i = avxSize; i < size; ++i)
    {
        dotProduct += vec1[i] * vec2[i];
    }

    return dotProduct;
}

测试情况

测试环境为VS2017、i7-12700K(支持AVX),测试使用的main函数及辅助代码如下:

std::vector<int> generateRandomVector(std::size_t size)
{
    std::vector<int> randomVector;
    randomVector.reserve(size);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> distribution(1, 100);

    for (std::size_t i = 0; i < size; ++i)
    {
        randomVector.push_back(distribution(gen));
    }

    return randomVector;
}

int main()
{
    // 指定向量大小
    std::size_t vectorSize = 100;
    int iterations = 1000000;
    // 生成两个随机向量
    std::vector<int> vector1 = generateRandomVector(vectorSize);
    std::vector<int> vector2 = generateRandomVector(vectorSize);

    // 测试AVX版本耗时
    int resultAVX;
    auto startAVX = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; i++)
        resultAVX = dotProductAVX(vector1, vector2);
    auto endAVX = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> durationAVX = endAVX - startAVX;

    // 测试普通版本耗时
    int resultNoAVX;
    auto startNoAVX = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; i++)
        resultNoAVX = dotProductNoAVX(vector1, vector2);
    auto endNoAVX = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> durationNoAVX = endNoAVX - startNoAVX;

    // 输出结果和耗时
    std::cout << "Dot product without AVX: " << resultNoAVX << std::endl;
    std::cout << "Time taken without AVX: " << durationNoAVX.count() << " seconds" << std::endl;

    std::cout << "Dot product with AVX: " << resultAVX << std::endl;
    std::cout << "Time taken with AVX: " << durationAVX.count() << " seconds" << std::endl;

    return 0;
}

测试结果显示两个版本速度大致相同,仅AVX版本偶尔快10-20%。性能分析发现,AVX版本中将寄存器结果存入数组再逐个累加的步骤耗时较长,需要优化。

优化方案

1. 寄存器内完成水平求和,避免内存操作

原来的代码需要把AVX寄存器中的8个int结果存入内存数组,再逐一累加,这会带来不必要的内存读写开销。可以使用AVX的水平加法指令_mm256_hadd_epi32在寄存器内完成求和:

优化后的AVX函数示例:

int dotProductAVXOptimized(const std::vector<int>& vec1, const std::vector<int>& vec2)
{
    std::size_t size = vec1.size();
    std::size_t avxSize = size / 8 * 8;

    __m256i sum = _mm256_setzero_si256();

    for (std::size_t i = 0; i < avxSize; i += 8)
    {
        __m256i vec1_part = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&vec1[i]));
        __m256i vec2_part = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&vec2[i]));
        sum = _mm256_add_epi32(sum, _mm256_mullo_epi32(vec1_part, vec2_part));
    }

    // 寄存器内水平求和8个int元素
    __m256i hsum1 = _mm256_hadd_epi32(sum, sum);
    __m256i hsum2 = _mm256_hadd_epi32(hsum1, hsum1);
    // 提取最终求和结果
    int dotProduct = _mm_cvtsi128_si32(_mm256_castsi256_si128(hsum2)) + 
                     _mm_extract_epi32(_mm256_extractf128_si256(hsum2, 1), 0);

    // 处理剩余元素
    for (std::size_t i = avxSize; i < size; ++i)
    {
        dotProduct += vec1[i] * vec2[i];
    }

    return dotProduct;
}

2. 检查编译器自动向量化

VS2017在/O2优化级别下,普通循环版本可能已经被编译器自动转换为AVX指令,导致手动AVX版本优势不明显。可以在普通循环前添加#pragma loop(no_vector)禁用自动向量化,测试手动AVX的真实性能。

3. 增大测试向量尺寸

当前测试的向量大小为100,AVX仅处理96个元素(12次循环),初始化和收尾操作的开销占比过高,导致并行优势无法体现。换成更大的向量尺寸(比如10000或更大),AVX的并行计算优势会更显著。

4. 内存对齐优化

当前使用_mm256_loadu_si256进行非对齐加载,如果能保证向量内存是32字节对齐的(AVX寄存器大小),可以改用_mm256_load_si256,减少对齐检查的开销。可以通过自定义vector分配器,使用std::aligned_alloc分配对齐内存来实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 06:55:58