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

GCC为何生成减1后与2比较的汇编?2的幂次cmp指令更快吗?

问题背景

我在编写将屏幕清空为指定颜色的代码,对应的C++实现如下:

void clear_screen(unsigned int color, void *memory, int height, int width) {
  unsigned int *pixel = (unsigned int *)memory;
  for (auto y = 0; y < height; y++)
    for (auto x = 0; x < width; x++)
      *pixel++ = color;
}

我使用g++编译、objconv工具生成了对应的汇编代码,以下是生成结果,我也为部分我理解的指令添加了注释:

renderer_clear_screen:
        push    r13                                     
        push    r12                                     
        push    rbp                                     
        push    rdi                                     
        push    rsi                                     
        push    rbx                                     
        mov     r11d, ecx            ; 将color存入r11d
        mov     ebx, r8d             ; 将height存入ebx
        mov     rcx, rdx             ; 内存指针存入rcx
        test    r8d, r8d             ; 
        jle     _cls_return          ; 宽或高为0时直接返回
        test    r9d, r9d             ; (比如窗口最小化的情况)
        jle     _cls_return          ;
        mov     r8d, r9d             
        mov     esi, r9d             ; esi = width
        mov     edi, r9d             ; edi = width
        xor     r10d, r10d           ; r10d = 0,外层循环计数器
        shr     esi, 2               
        movd    xmm1, r11d           ; 将color低32位存入xmm1
        lea     r12d, [r9-1]         ; r12d = width - 1
        shl     rsi, 4               
        mov     ebp, r8d             
        shl     rdi, 2               
        pshufd  xmm0, xmm1, 0        ; 将xmm1中的32位颜色值复制4份填满xmm0
        shl     rbp, 2               

ALIGN   8
?_001:  cmp     r12d, 2                                
        jbe     ?_006                ; 若width - 1 <= 2,跳转到标量处理分支
        mov     rax, rcx             
        lea     rdx, [rcx+rsi]       

ALIGN   8
?_002:  movups  oword [rax], xmm0    ; 一次写16字节(4个像素)
        add     rax, 16              
        cmp     rdx, rax             
        jnz     ?_002                ; 循环写完当前行所有整16字节块
        lea     rdx, [rcx+rbp]       
        mov     eax, r8d             
        cmp     r9d, r8d             
        jz      ?_004                
?_003:  lea     r13d, [rax+1H]       
        mov     dword [rdx], r11d    ; 写1个剩余像素
        cmp     r13d, r9d            
        jge     ?_004                
        add     eax, 2               
        mov     dword [rdx+4H], r11d ; 写第2个剩余像素
        cmp     r9d, eax             
        jle     ?_004                
        mov     dword [rdx+8H], r11d ; 写第3个剩余像素
?_004:  add     r10d, 1              
        add     rcx, rdi             ; 指针移动到下一行起始位置
        cmp     ebx, r10d            
        jnz     ?_001                ; 外层循环,处理下一行
_cls_return: 
        pop     rbx                  
        pop     rsi                  
        pop     rdi                  
        pop     rbp                  
        pop     r12                  
        pop     r13                  ; 恢复所有保存的寄存器
        ret                          ; 返回

?_006:  
        mov     rdx, rcx             
        xor     eax, eax             
        jmp     ?_003                ; 直接进入逐像素写逻辑
疑问

可以看到在?_001标签处,编译器将width - 1与2做比较,该逻辑等价于直接将width与3比较。我的疑问是:开启-O3优化等级时,编译器为什么选择和2比较,还额外消耗一条lea指令将width - 1存入r12d?
我目前猜测的原因是汇编中cmp指令对比2的幂次时执行速度更快,还是说这只是编译器的实现特性?

解答

这和「cmp对比2的幂次执行速度更快」没有任何关系,纯粹是GCC开启O3优化做循环向量化拆分时的通用代码生成逻辑,不存在你以为的额外性能损耗:

  • 这条lea r12d, [r9-1]不是额外冗余指令。编译器生成16字节XMM向量批量写逻辑时,本来就要计算「行宽无法被向量长度整除时的剩余像素边界」,width-1是后续标量收尾分支本来就要用到的值,提前算好存入被调用者保存的r12寄存器,是为了避免外层y循环每轮迭代都重复计算这个值,反而是优化手段。
  • 比较width-1 <= 2和width <=3虽然逻辑等价,但前者是编译器做循环拆分的通用判断模板:这里XMM寄存器一次可以写入4个4字节像素,只要行宽小于4,就直接跳转到逐像素处理的标量分支,完全不走SIMD批量写循环。用n-1和阈值比较是编译器判断数值上界的惯用生成模式,不需要额外调整标志位判断逻辑,和直接比较3相比,分支预测效率、指令执行速度没有任何区别。
  • 你可以观察后面的?_003标量处理分支,本身就是按写1个、写2个、写3个的顺序做分支跳转,刚好覆盖行宽为1、2、3的所有情况,不需要额外处理边界,这个判断和后续收尾逻辑是完全配套的,不是孤立生成的代码。
  • x86架构下cmp指令对所有普通立即数的比较,延迟和吞吐量完全一致,不存在比较2比比较3更快的说法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 06:27:27