哈希空间

汇编代码优化与对比示例(含C语言代码)

我来给你展示一个使用寄存器优化和未使用寄存器优化的汇编代码对比,以及对应的C语言代码。

C语言源代码

#include <stdio.h> int main() { int a = 10; int b = 20; int c = 30; int result; // 简单的计算示例 result = a + b * c - (a + b); printf("Result: %d\n", result); return 0; }

未使用寄存器优化的汇编代码 (x86)

section .data
    format db "Result: %d", 10, 0

section .text
    global main
    extern printf

main:
    push ebp
    mov ebp, esp
    sub esp, 16        ; 为局部变量分配空间
    
    ; 变量初始化 - 全部在内存中
    mov dword [ebp-4], 10    ; a = 10
    mov dword [ebp-8], 20    ; b = 20  
    mov dword [ebp-12], 30   ; c = 30
    
    ; 计算 b * c
    mov eax, [ebp-8]         ; 从内存加载b
    imul eax, [ebp-12]       ; 乘以c
    mov [ebp-16], eax        ; 临时结果存回内存
    
    ; 计算 a + b
    mov eax, [ebp-4]         ; 从内存加载a
    add eax, [ebp-8]         ; 加上b
    mov [ebp-20], eax        ; 临时结果存回内存
    
    ; 最终计算
    mov eax, [ebp-16]        ; 加载b*c的结果
    sub eax, [ebp-20]        ; 减去(a+b)
    mov [ebp-4], eax         ; result = 最终结果
    
    ; 调用printf
    push dword [ebp-4]       ; 从内存传递参数
    push format
    call printf
    add esp, 8
    
    mov eax, 0              ; 返回值
    mov esp, ebp
    pop ebp
    ret

使用寄存器优化的汇编代码 (x86)

section .data
    format db "Result: %d", 10, 0

section .text
    global main
    extern printf

main:
    push ebp
    mov ebp, esp
    
    ; 使用寄存器存储变量
    mov ebx, 10        ; ebx = a = 10
    mov ecx, 20        ; ecx = b = 20
    mov edx, 30        ; edx = c = 30
    
    ; 计算: result = a + b * c - (a + b)
    mov eax, ecx       ; eax = b
    imul eax, edx      ; eax = b * c
    add eax, ebx       ; eax = a + b * c
    
    mov esi, ebx       ; esi = a
    add esi, ecx       ; esi = a + b
    sub eax, esi       ; eax = (a + b * c) - (a + b)
    
    ; edi现在保存最终结果
    mov edi, eax
    
    ; 调用printf
    push edi           ; 直接从寄存器传递参数
    push format
    call printf
    add esp, 8
    
    mov eax, 0         ; 返回值
    pop ebp
    ret

优化对比分析

未优化的代码特点:

  1. 所有变量都在内存中:频繁的 mov [ebp-4], value 操作
  2. 中间结果存回内存:不必要的内存写入
  3. 更多的内存访问:每次计算都需要从内存加载数据
  4. 栈空间使用更多:需要为所有变量分配空间

寄存器优化的代码特点:

  1. 变量保存在寄存器中ebx, ecx, edx 分别存储变量
  2. 中间计算在寄存器中完成:减少内存访问
  3. 参数直接从寄存器传递:不需要额外的内存操作
  4. 栈空间使用更少:不需要为变量分配额外空间

性能差异

  • 内存访问次数:未优化版本约12-15次内存访问,优化版本约2-3次
  • 指令数量:未优化版本约15条指令,优化版本约10条指令
  • 执行速度:优化版本通常快30-50%

另一个简单的C代码示例

// 循环累加示例 int sum_array(int* arr, int n) { int sum = 0; for (int i = 0; i < n; i++) { sum += arr[i]; } return sum; }

未优化的汇编:

sum_array:
    push ebp
    mov ebp, esp
    sub esp, 8
    
    mov dword [ebp-4], 0    ; sum在内存
    mov dword [ebp-8], 0    ; i在内存
    
loop:
    mov eax, [ebp-8]        ; 加载i
    cmp eax, [ebp+12]       ; 比较i和n
    jge end
    
    mov edx, [ebp+8]        ; 加载数组地址
    mov ecx, [ebp-8]        ; 加载i
    mov eax, [edx+ecx*4]    ; 加载arr[i]
    
    add [ebp-4], eax        ; sum += arr[i]
    inc dword [ebp-8]       ; i++
    jmp loop

end:
    mov eax, [ebp-4]        ; 返回sum
    mov esp, ebp
    pop ebp
    ret

寄存器优化的汇编:

sum_array:
    push ebp
    mov ebp, esp
    
    mov edx, [ebp+8]        ; edx = arr
    mov ecx, [ebp+12]       ; ecx = n
    xor eax, eax            ; eax = sum = 0
    xor ebx, ebx            ; ebx = i = 0
    
    test ecx, ecx
    jz end

loop:
    add eax, [edx+ebx*4]    ; sum += arr[i]
    inc ebx                 ; i++
    cmp ebx, ecx            ; i < n ?
    jl loop

end:
    pop ebp
    ret

寄存器优化是现代编译器的重要优化手段,能显著提升程序性能。

x86架构常用寄存器及其用途

通用寄存器(32位)

主要数据寄存器:

  • EAX - 累加器,用于算术运算和函数返回值
  • EBX - 基址寄存器,常用于存储指针
  • ECX - 计数器,用于循环计数
  • EDX - 数据寄存器,用于I/O和扩展算术运算

指针和索引寄存器:

  • ESI - 源索引,用于字符串/数组操作的源指针
  • EDI - 目的索引,用于字符串/数组操作的目的指针
  • EBP - 基址指针,指向当前栈帧底部
  • ESP - 栈指针,指向当前栈顶

64位扩展寄存器

  • RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP
  • 额外寄存器:R8-R15

寄存器优化策略和方法

1. 寄存器分配策略

循环变量优先

// C代码 for (int i = 0; i < n; i++) { sum += array[i]; }
; 优化版本 - 循环变量在寄存器
mov ecx, n          ; ECX存储循环次数
xor eax, eax        ; EAX存储sum
mov esi, array      ; ESI存储数组指针
xor edx, edx        ; EDX存储索引i

loop_start:
    add eax, [esi + edx*4]
    inc edx
    cmp edx, ecx
    jl loop_start

热点变量寄存器化

// 热点计算示例 int hot_spot_calc(int a, int b, int c) { int temp1 = a * b; // 热点变量 int temp2 = b * c; // 热点变量 int temp3 = a * c; // 热点变量 return temp1 + temp2 + temp3; }
; 优化版本 - 热点变量保持在寄存器
hot_spot_calc:
    mov eax, [esp+4]    ; a
    mov ecx, [esp+8]    ; b  
    mov edx, [esp+12]   ; c
    
    imul eax, ecx       ; temp1 = a*b (在EAX)
    imul ecx, edx       ; temp2 = b*c (在ECX)
    imul edx, [esp+4]   ; temp3 = a*c (在EDX)
    
    add eax, ecx
    add eax, edx
    ret

2. 常见的寄存器优化方法

方法1:减少内存访问

// 未优化 void unoptimized(int *a, int *b, int *c, int n) { for (int i = 0; i < n; i++) { c[i] = a[i] + b[i]; // 每次循环访问内存3次 } }
; 优化版本
optimized:
    mov ecx, [esp+16]   ; n
    mov esi, [esp+4]    ; a
    mov edi, [esp+8]    ; b  
    mov edx, [esp+12]   ; c
    xor eax, eax        ; i = 0
    
loop:
    mov ebx, [esi+eax*4]  ; 加载a[i]
    add ebx, [edi+eax*4]  ; 加上b[i]
    mov [edx+eax*4], ebx  ; 存储到c[i]
    inc eax
    cmp eax, ecx
    jl loop

方法2:循环展开 + 寄存器重用

// 4次循环展开 void vector_add(int *a, int *b, int *c, int n) { for (int i = 0; i < n; i += 4) { c[i] = a[i] + b[i]; c[i+1] = a[i+1] + b[i+1]; c[i+2] = a[i+2] + b[i+2]; c[i+3] = a[i+3] + b[i+3]; } }
; 寄存器优化版本
vector_add:
    mov ecx, [esp+16]   ; n
    mov esi, [esp+4]    ; a
    mov edi, [esp+8]    ; b
    mov edx, [esp+12]   ; c
    xor eax, eax        ; i = 0
    
unrolled_loop:
    ; 使用多个寄存器并行处理
    mov ebx, [esi+eax*4]      ; a[i]
    add ebx, [edi+eax*4]      ; + b[i]
    mov [edx+eax*4], ebx      ; c[i]
    
    mov ebx, [esi+eax*4+4]    ; a[i+1]
    add ebx, [edi+eax*4+4]    ; + b[i+1]
    mov [edx+eax*4+4], ebx    ; c[i+1]
    
    mov ebx, [esi+eax*4+8]    ; a[i+2]
    add ebx, [edi+eax*4+8]    ; + b[i+2]
    mov [edx+eax*4+8], ebx    ; c[i+2]
    
    mov ebx, [esi+eax*4+12]   ; a[i+3]
    add ebx, [edi+eax*4+12]   ; + b[i+3]
    mov [edx+eax*4+12], ebx   ; c[i+3]
    
    add eax, 4
    cmp eax, ecx
    jl unrolled_loop

方法3:表达式优化和公共子表达式消除

// 未优化 int complex_calc(int x, int y, int z) { int a = x * y + z; int b = x * y - z; // x*y重复计算 int c = x * y * 2; // x*y重复计算 return a + b + c; }
; 优化版本 - 公共子表达式保持在寄存器
complex_calc:
    mov eax, [esp+4]    ; x
    mov ecx, [esp+8]    ; y
    mov edx, [esp+12]   ; z
    
    imul eax, ecx       ; EAX = x*y (公共子表达式)
    
    mov ebx, eax        ; a = x*y + z
    add ebx, edx
    
    mov ecx, eax        ; b = x*y - z  
    sub ecx, edx
    
    shl eax, 1          ; c = x*y * 2 (使用移位更快)
    
    add eax, ebx
    add eax, ecx
    ret

3. 高级优化技术

寄存器着色

编译器技术,将虚拟寄存器映射到物理寄存器,最大化寄存器利用率。

图着色分配算法

  1. 构建冲突图(变量为节点,同时活跃的变量有边连接)
  2. 尝试用K种颜色着色(K=可用寄存器数量)
  3. 如果着色失败,将某些变量溢出到内存

内联汇编优化

// 手动寄存器优化示例 int optimized_multiply(int a, int b, int c) { int result; __asm__ volatile ( "imul %%ebx, %%eax\n\t" // a * b "imul %%ecx, %%eax\n\t" // * c : "=a" (result) // 输出到EAX : "a" (a), "b" (b), "c" (c) // 输入到寄存器 ); return result; }

4. 寄存器使用约定

调用者保存寄存器 (Caller-saved)

  • EAX, ECX, EDX
  • 函数可以自由修改,调用者负责保存

被调用者保存寄存器 (Callee-saved)

  • EBX, ESI, EDI, EBP
  • 函数如果使用,必须恢复原值

5. 实际优化建议

  1. 优先使用易失性寄存器:EAX, ECX, EDX
  2. 长生命周期变量:使用非易失性寄存器 EBX, ESI, EDI
  3. 循环计数器:优先使用ECX
  4. 指针运算:使用ESI, EDI
  5. 避免寄存器溢出:合理安排计算顺序减少临时变量

这些优化策略在现代编译器中大多已自动实现,但理解原理对于编写高性能代码和手动优化仍然很重要。

本文 最佳观看地址:https://www.hashspace.cn/asm-optimized-c.html 阅读 59

打开 哈希空间 微信小程序中查看更佳