Site Tools


wiki:gcc-performance

Table of Contents

GCC Performance Tuning

Performance optimization is iterative: measure, identify bottlenecks, apply targeted optimizations, measure again. Start with compiler flags, then optimize code if needed.

Baseline: Compile with standard flags and benchmark:

gcc -O3 -march=native -g -o app app.c
./app < large_input      # time this

Establish a baseline. Everything else is a delta from this.

Profile to find bottlenecks:

perf record -g ./app < input
perf report

Use perf to identify which functions consume the most CPU. This tells you where to focus optimization effort.

Compiler-level optimization:

  1. Vectorization: Ensure hot loops are vectorized:
gcc -O3 -march=native -fopt-info-vec-missed -c hot_loop.c | grep missed

If loops that should vectorize don't, restructure the code (improve memory access patterns, eliminate data dependencies).

  1. Inlining: Hot function calls should be inlined:
gcc -O3 -Winline -o app app.c 2>&1 | grep inline

If important functions aren't inlined, try -finline-functions or mark them explicitly inline.

  1. LTO: For multi-file programs:
gcc -O3 -flto -o app a.c b.c c.c  # enables cross-file optimization
  1. PGO: Profile-guided optimization uses real execution data:
gcc -O3 -fprofile-generate -o app app.c
./app < training_input      # profile with representative data
gcc -O3 -fprofile-use -o app app.c  # recompile optimizing for actual patterns

Code-level optimization: If compiler optimizations aren't enough:

  1. Memory access patterns: Sequential access (unit stride) is faster than indirect. Restructure data layouts (structure-of-arrays instead of array-of-structures).
  2. Loop unrolling: Manually unroll small loops to increase instruction-level parallelism:
// Loop unrolled 4x
for (int i = 0; i < n; i += 4) {
    result[i] = a[i] + b[i];
    result[i+1] = a[i+1] + b[i+1];
    result[i+2] = a[i+2] + b[i+2];
    result[i+3] = a[i+3] + b[i+3];
}
  1. Cache optimization: Improve data locality. Work on cache-sized chunks, reuse loaded data before eviction.
  2. Reduce branches: Data-dependent branches mispredict and stall pipelines. Use conditional moves or branchless algorithms.

Benchmarking best practices:

# Run multiple times and average
for i in {1..5}; do
    time ./app < input
done
 
# Warm up CPU, reduce OS noise
./app < warmup_input    # warm up
perf stat -r 10 ./app < input  # average 10 runs

Run multiple iterations and average—system noise makes single runs unreliable.

Amdahl's law: Optimize the hottest functions first. A 2x speedup in code that consumes 50% of time is a 1.33x overall speedup. Focus optimization effort on the bottleneck.

Trade-offs: Faster code often means larger binary (from aggressive inlining, unrolling) or more memory (LTO compilation). Verify the trade-off is worth it—measure both speed and size.

Compiler vs. hardware: Sometimes the bottleneck is hardware-limited (memory bandwidth, cache size), not compiler quality. In that case, code restructuring (improving cache locality, reducing bandwidth usage) helps more than compiler flags.

wiki/gcc-performance.md · Last modified: by 127.0.0.1