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

混合4宽与8宽指令是否严重影响AVX性能?代码优化求助

AVX版本性能远低于SSE4版本的瓶颈分析与优化建议

我编写的AVX代码运行速度远慢于SSE4版本,理论上AVX版本指令数翻倍但处理量也翻倍,性能应与SSE4版本相当,但实际运行速度堪比标量版本。以下是各版本的汇编代码:

GCC 13.1生成的SSE4循环汇编

.L6:
        movaps  xmm1, XMMWORD PTR [rbx+rsi]
        movaps  xmm3, XMMWORD PTR [rbp+0+rsi]
        movaps  xmm2, xmm4
        lea     eax, [0+rcx*4]
        movd    xmm0, eax
        add     rcx, 1
        add     rsi, 16
        addps   xmm3, xmm1
        cmpleps xmm1, xmm5
        pshufd  xmm0, xmm0, 0
        paddd   xmm0, xmm6
        cmpleps xmm2, xmm3
        pand    xmm1, xmm2
        movmskps edi, xmm1
        mov     rax, rdi
        sal     rdi, 4
        pshufb  xmm0, XMMWORD PTR shufmasks.0[rdi]
        popcnt  eax, eax
        movups  XMMWORD PTR [r8], xmm0
        lea     r8, [r8+rax*4]
        cmp     rcx, rdx
        jne     .L6

GCC 13.1生成的AVX循环汇编

.L6:
        lea     eax, [0+rcx*8]
        vmovaps ymm7, YMMWORD PTR [rbx+rsi]
        xor     r10d, r10d
        add     rcx, 1
        vmovd   xmm1, eax
        xor     eax, eax
        vpshufd xmm0, xmm1, 0
        vcmpleps ymm6, ymm7, ymm3
        vmovdqa xmm1, xmm0
        vpaddd  xmm0, xmm0, xmm5
        vpaddd  xmm1, xmm1, xmm2
        vinsertf128 ymm0, ymm0, xmm1, 0x1
        vaddps  ymm1, ymm7, YMMWORD PTR [r12+rsi]
        add     rsi, 32
        vcmpleps ymm1, ymm4, ymm1
        vandps  ymm1, ymm1, ymm6
        vmovaps xmm6, xmm0
        vextractf128 xmm0, ymm0, 0x1
        vmovaps xmm7, xmm1
        vextractf128 xmm1, ymm1, 0x1
        vmovmskps edx, xmm7
        popcnt  eax, edx
        sal     rdx, 4
        vpshufb xmm6, xmm6, XMMWORD PTR shufmasks.0[rdx]
        vmovmskps edx, xmm1
        popcnt  r10d, edx
        sal     rdx, 4
        vmovdqa XMMWORD PTR [rsp+32], xmm6
        vpshufb xmm0, xmm0, XMMWORD PTR shufmasks.0[rdx]
        movsx   rdx, eax
        add     eax, r10d
        vmovups XMMWORD PTR [rsp+32+rdx*4], xmm0
        vmovdqa ymm6, YMMWORD PTR [rsp+32]
        cdqe
        vmovdqu YMMWORD PTR [rdi], ymm6
        lea     rdi, [rdi+rax*4]
        cmp     rcx, r8
        jne     .L6

GCC 12.2.0生成的AVX循环汇编

.L4:
    vmovaps ymm11, YMMWORD PTR [rbx+rsi]
    vaddps  ymm12, ymm11, YMMWORD PTR [r12+rsi]
    xor eax, eax
    add rsi, 32
    lea r11d, 0[0+rcx*8]
    add rcx, 1
    vcmpleps    ymm14, ymm11, ymm3
    vmovd   xmm1, r11d
    xor r11d, r11d
    vcmpleps    ymm13, ymm4, ymm12
    vpshufd xmm0, xmm1, 0
    vpaddd  xmm7, xmm0, xmm5
    vpaddd  xmm9, xmm0, xmm8
    vinsertf128 ymm10, ymm7, xmm9, 0x1
    vandps  ymm15, ymm13, ymm14
    vextractf128    xmm0, ymm10, 0x1
    vextractf128    xmm1, ymm15, 0x1
    vmovmskps   r13d, xmm15
    vmovmskps   r14d, xmm1
    popcnt  eax, r13d
    sal r13, 4
    movsx   rdx, eax
    popcnt  r11d, r14d
    sal r14, 4
    vpshufb xmm7, xmm10, XMMWORD PTR [r8+r13]
    add eax, r11d
    vpshufb xmm9, xmm0, XMMWORD PTR [r8+r14]
    vmovdqa XMMWORD PTR 32[rsp], xmm7
    cdqe
    vmovups XMMWORD PTR 32[rsp+rdx*4], xmm9
    vmovdqa ymm10, YMMWORD PTR 32[rsp]
    vmovdqu YMMWORD PTR [rdi], ymm10
    lea rdi, [rdi+rax*4]
    cmp rcx, r9
    jne .L4

核心性能瓶颈分析

  1. 栈内存临时存储的额外开销
    AVX版本频繁使用栈内存([rsp+32])作为临时缓冲区,存储拆分后的SSE结果再合并。栈内存的读写会引入延迟,且vmovdqu非对齐加载指令在循环中反复触发,进一步增加内存访问开销。而SSE版本直接将结果写入目标寄存器,无中间栈存储步骤。

  2. YMM/XMM寄存器切换的流水线断裂
    代码中频繁调用vextractf128和vinsertf128在YMM与XMM寄存器间拆分、合并数据,这类指令本身有延迟,还会导致CPU的AVX与SSE执行单元频繁切换,破坏指令流水线的连续性,部分CPU还会产生模式切换的额外周期损耗。

  3. 数据依赖链过长导致流水线停滞
    AVX版本拆分后的popcnt、pshufb操作形成了更长的依赖链:从YMM拆分到XMM,再做掩码计算、洗牌,最后合并写入。相比SSE版本的单条依赖链,AVX的两条并行依赖链加合并步骤,导致指令等待时间变长,流水线停滞概率升高。

  4. 寄存器调度效率低下
    AVX版本使用了更多寄存器,但编译器生成的代码指令顺序不合理(如vaddps放在插入操作之后),没有充分利用CPU的乱序执行能力,导致执行单元空闲等待。


优化建议

  1. 移除栈内存临时存储
    直接用两个XMM寄存器存储拆分后的结果,最后通过vinsertf128合并为YMM寄存器再写入目标内存,彻底避免栈的读写操作。

  2. 重排指令顺序,利用乱序执行
    将浮点运算(vmovaps、vaddps、vcmpleps)与整数拆分操作并行安排,让CPU的浮点单元和整数单元同时工作,减少流水线停滞。

  3. 优化掩码提取逻辑
    使用vpmovmskb直接将YMM寄存器的掩码转为64位整数,再拆分为两个32位整数处理,替代vextractf128+vmovmskps的组合,减少指令数量。

  4. 调整编译器优化选项
    尝试添加-mavx2(CPU支持时)、-O3、-ffast-math、-fno-tree-vectorize等选项,引导编译器生成更高效的AVX代码;也可尝试clang编译器,其AVX指令调度通常更优。

  5. 合并结果写入步骤
    计算出两个XMM的结果后,直接合并为YMM写入目标内存,减少内存操作次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 04:17:02