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

自研AVX SIMD向量加法为何慢于std::transform?是否实现有误?

SIMD加法性能异常问题排查

环境与准备

本人刚接触SIMD,若有错误恳请见谅。
开发环境:Windows Visual Studio 2022,MSVC ISO C++20标准,处理器为11th Gen Intel(R) Core(TM) i7-11370H @ 3.30GHz。

在使用AVX前,通过检查ECX寄存器第28位确认PC支持AVX:

bool supportsAVX()
{
    int cpuInfo[4] = { 0 };
    __cpuid(cpuInfo, 1);
    return (cpuInfo[2] & (1 << 28)) != 0;
}

为确保向量缓冲区按32字节对齐,参考现代对齐内存分配方案实现了对齐分配器,据此定义适用于AVX的对齐向量:

template<typename T, std::size_t ALIGNMENT_IN_BYTES = 32>
using Aligned32Vector = std::vector<T, AlignedAllocator<T, ALIGNMENT_IN_BYTES> >;

实现代码

AVX SIMD加法函数

#include "aligned_vector.hpp"
#include <immintrin.h>  // For SSE/AVX intrinsics

void simd_add_sd_float_avx(const simd_util::Aligned32Vector<double>& a, const simd_util::Aligned32Vector<double>& b, simd_util::Aligned32Vector<double>& c)
{
    size_t const total_size = a.size();
    constexpr size_t working_width = 32 / sizeof(double);

    size_t i = 0;

    // AVX SIMD loop
    for (; i < total_size - working_width; i += working_width)
    {  // Process 4 double at a time
        // Load
        __m256d va = _mm256_load_pd(&a[i]);  // Load 4 double
        __m256d vb = _mm256_load_pd(&b[i]);  // Load 4 double

        // Perform SIMD addition
        __m256d vsum = _mm256_add_pd(va, vb);  // Add 4 double in parallel

        // Store the result back into the 'result' array
        _mm256_store_pd(&c[i], vsum);  // Store 4 double
    }

    // Handle leftovers
    if (i < total_size)
    {
        size_t remaining = total_size - i;
        alignas(32) double mask_data[4] = { 0.0 };
        
        __m256d mask = _mm256_set_pd(
            remaining > 3 ? -1.0 : 0.0,
            remaining > 2 ? -1.0 : 0.0,
            remaining > 1 ? -1.0 : 0.0,
            remaining > 0 ? -1.0 : 0.0
        );
        // over reading and adding, but who cares we good as long as we dont use them
        __m256d va = _mm256_loadu_pd(&a[i]);
        __m256d vb = _mm256_loadu_pd(&b[i]);
        __m256d vr = _mm256_add_pd(va, vb);
        __m256d existing = _mm256_setzero_pd(); // Set to zeros
        __m256d blended = _mm256_blendv_pd(existing, vr, mask);
    
        // Scalar write-back
        alignas(32) double temp[4];
        _mm256_storeu_pd(temp, blended);
        for (int j = 0; j < remaining; ++j)
        {
            c[i + j] = temp[j];
        }
    }
}

标量加法函数

#include<vector>
#include<algorithm>

void add_vector_float_normal(const std::vector<double>& a, const std::vector<double>& b, std::vector<double>& c) 
{
    std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::plus<double>());
}

测试方案与结果

测试用例:创建两个大小为1000000、值为1的向量,相加后赋值给第三个同大小向量,使用chrono统计耗时,循环执行10000次。

标量测试代码

std::vector<double> a(1000000,1);
std::vector<double> b(1000000,1);
std::vector<double> c(1000000,0);

auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i)
{
    add_vector_float_normal(a,b,c);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
double average_time_us = static_cast<double>(duration.count()) / iterations;

SIMD测试代码

simd_util::Aligned32Vector<double> a(1000000, 1);
simd_util::Aligned32Vector<double> b(1000000, 1);
simd_util::Aligned32Vector<double> c(1000000, 0);

auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i)
{
    simd_add_sd_float_avx(a, b, c);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
double average_time_us = static_cast<double>(duration.count()) / iterations;

测试结果显示标量版本始终优于SIMD版本:
测试结果

本人预期SIMD版本整体应优于标量版本,疑惑是否实现有误,或是剩余元素处理部分效率低下。

排查过程

编译器配置

Visual Studio 2022的编译器选项:

/permissive- /ifcOutput "x64\Release\" /GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"x64\Release\vc143.pdb" /Zc:inline /fp:precise /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /std:c17 /Gd /Oi /MD /std:c++20 /FC /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"x64\Release\simd_matrix.pch" /diagnostics:column 

vcxproj文件中的Release|x64配置:

<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <LanguageStandard>stdcpp20</LanguageStandard>
      <LanguageStandard_C>stdc17</LanguageStandard_C>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>

汇编代码分析

开启/O2优化后,std::transform的测试代码已被向量化,但使用的是SSE寄存器而非AVX寄存器。同时自研AVX实现反而更慢,汇编代码片段:

$LL33@add_vector:
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
    movups  xmm0, XMMWORD PTR [rcx]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
; Line 3763
    add rsi, 8
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
    movups  xmm1, XMMWORD PTR [rax]
    addpd   xmm1, xmm0
    movups  xmm0, XMMWORD PTR [rcx+16]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
; Line 3764
    movups  XMMWORD PTR [rdx], xmm1
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
    movups  xmm1, XMMWORD PTR [rax+16]
    addpd   xmm1, xmm0
    movups  xmm0, XMMWORD PTR [rcx+32]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm
; Line 3764
    movups  XMMWORD PTR [rdx+16], xmm1
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\xutility
; Line 501
    movups  xmm1, XMMWORD PTR [rax+32]
    addpd   xmm1, xmm0
    movups  xmm0, XMMWORD PTR [rcx+48]
; File C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\include\algorithm

发现循环展开

观察到MSVC生成的代码中存在循环展开,计划在自研AVX实现中加入循环展开,观察性能变化。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 01:57:31