Tối ưu hiệu suất với Compiler Optimization

Hiểu rõ về các mức độ tối ưu hóa của compiler (O0, O1, O2, O3, Os) và cách sử dụng chúng hiệu quả trong dự án C/C++

Tối ưu hiệu suất với Compiler Optimization

Tối ưu hiệu suất với Compiler Optimization

Compiler optimization là quá trình compiler biến đổi code để cải thiện hiệu suất, giảm kích thước binary, hoặc cả hai. Hiểu rõ về optimization giúp bạn viết code hiệu quả hơn và debug dễ dàng hơn.

Các mức độ Optimization

GCC và Clang cung cấp nhiều optimization levels:

-O0: No Optimization (Default)

gcc -O0 main.c -o main
  • Không có optimization
  • Compile nhanh nhất
  • Debug dễ dàng nhất
  • Dùng khi: Development và debugging

-O1: Basic Optimization

gcc -O1 main.c -o main
  • Optimization cơ bản
  • Không làm tăng đáng kể compile time
  • Cải thiện performance ~30-50%
  • Dùng khi: Development với performance concerns
gcc -O2 main.c -o main
  • Optimization level khuyến nghị cho production
  • Cân bằng giữa performance và compile time
  • Cải thiện performance ~100-200%
  • Dùng khi: Production builds (default choice)

-O3: Aggressive Optimization

gcc -O3 main.c -o main
  • Optimization mạnh mẽ nhất
  • Có thể tăng code size
  • Cải thiện performance ~150-300%
  • Một số optimization có thể unsafe
  • Dùng khi: Performance-critical code

-Os: Optimize for Size

gcc -Os main.c -o main
  • Tối ưu hóa kích thước binary
  • Quan trọng cho embedded systems
  • Trade-off: có thể chậm hơn O2/O3
  • Dùng khi: Memory-constrained systems

-Ofast: Disregard Standards

gcc -Ofast main.c -o main
  • Bao gồm -O3 + non-standard optimizations
  • Có thể vi phạm IEEE floating-point standards
  • Cẩn thận: Chỉ dùng khi hiểu rõ trade-offs

Ví dụ Thực Tế

Xét hàm tính tổng array:

// source.c
#include <stdio.h>
 
int sum_array(int* arr, int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return sum;
}
 
int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int result = sum_array(numbers, 5);
    printf("Sum: %d\n", result);
    return 0;
}

Assembly với -O0 (No optimization)

gcc -O0 -S source.c -o output_O0.s
sum_array:
    push    rbp
    mov     rbp, rsp
    mov     QWORD PTR [rbp-24], rdi
    mov     DWORD PTR [rbp-28], esi
    mov     DWORD PTR [rbp-4], 0      # sum = 0
    mov     DWORD PTR [rbp-8], 0      # i = 0
    jmp     .L2
.L3:
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    lea     rdx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rdx
    mov     eax, DWORD PTR [rax]
    add     DWORD PTR [rbp-4], eax    # sum += arr[i]
    add     DWORD PTR [rbp-8], 1      # i++
.L2:
    mov     eax, DWORD PTR [rbp-8]
    cmp     eax, DWORD PTR [rbp-28]   # i < size
    jl      .L3
    mov     eax, DWORD PTR [rbp-4]
    pop     rbp
    ret

Assembly với -O3 (Aggressive optimization)

gcc -O3 -S source.c -o output_O3.s
sum_array:
    test    esi, esi
    jle     .L4
    lea     eax, [rsi-1]
    cmp     eax, 2
    jbe     .L8
    mov     edx, esi
    pxor    xmm0, xmm0               # Vector register
    xor     eax, eax
    shr     edx, 2
    sal     rdx, 4
.L5:                                  # Vectorized loop (SIMD)
    movdqu  xmm1, XMMWORD PTR [rdi+rax]
    add     rax, 16
    paddd   xmm0, xmm1               # Parallel add (4 integers at once)
    cmp     rdx, rax
    jne     .L5
    # ... horizontal sum ...
    ret
.L4:
    xor     eax, eax
    ret

Kết quả: Code được vectorized (SIMD), xử lý 4 integers cùng lúc thay vì 1!

Specific Optimizations

Loop Unrolling

// Original
for (int i = 0; i < 100; i++) {
    arr[i] = i;
}
 
// Compiler unrolls to:
for (int i = 0; i < 100; i += 4) {
    arr[i] = i;
    arr[i+1] = i+1;
    arr[i+2] = i+2;
    arr[i+3] = i+3;
}

Function Inlining

inline int square(int x) {
    return x * x;
}
 
int main() {
    int result = square(5);  // Compiler inlines → result = 5 * 5
}

Constant Folding

int x = 2 + 3 * 4;  // Compiler computes at compile-time → x = 14

Dead Code Elimination

int compute() {
    int x = expensive_calculation();  // Eliminated if x not used
    return 42;
}

Benchmarking Optimizations

# Compile với các optimization levels khác nhau
gcc -O0 benchmark.c -o benchmark_O0
gcc -O2 benchmark.c -o benchmark_O2
gcc -O3 benchmark.c -o benchmark_O3
 
# Đo thời gian execution
time ./benchmark_O0  # 2.5 seconds
time ./benchmark_O2  # 0.9 seconds (2.7x faster!)
time ./benchmark_O3  # 0.7 seconds (3.5x faster!)

Cẩm Nang Sử Dụng

Development

gcc -O0 -g -Wall -Wextra main.c -o main
  • -O0: No optimization, debug friendly
  • -g: Debug symbols
  • -Wall -Wextra: All warnings

Production

gcc -O2 -DNDEBUG -march=native main.c -o main
  • -O2: Balanced optimization
  • -DNDEBUG: Disable assertions
  • -march=native: Optimize for your CPU

Performance-Critical

gcc -O3 -march=native -flto -ffast-math main.c -o main
  • -O3: Aggressive optimization
  • -flto: Link-Time Optimization
  • -ffast-math: Fast floating-point (unsafe for some apps)

Embedded Systems

gcc -Os -ffunction-sections -fdata-sections main.c -o main
  • -Os: Optimize for size
  • -ffunction-sections -fdata-sections: Enable garbage collection of unused functions

Lưu Ý Quan Trọng

Volatile Keyword

volatile int sensor_value;  // Compiler won't optimize away reads/writes

Optimization Barriers

asm volatile("" ::: "memory");  // Prevents reordering

Debugging Optimized Code

gcc -O2 -g3 main.c -o main  # -g3: maximum debug info
gdb main

Kết luận

Compiler optimization là công cụ mạnh mẽ có thể cải thiện performance đáng kể. Key takeaways:

  • Development: Dùng -O0 để debug dễ dàng
  • Production: Dùng -O2 là lựa chọn an toàn nhất
  • Performance-critical: Cân nhắc -O3 nhưng cần test kỹ
  • Embedded: Dùng -Os để tiết kiệm memory

Luôn benchmark code của bạn với optimization levels khác nhau!

5 min read
CC++OptimizationCompilerPerformance

Bài viết liên quan