# GCC Optimization Levels **Optimization levels** (`-O0` through `-O3`, `-Os`, `-Ofast`) enable bundles of passes that transform code. Each level adds more aggressive optimizations, trading compilation time and code size for runtime speed. `-O0` (default) disables optimization entirely. Code compiles instantly but runs slowly and produces predictable output for debugging. Essential for [[gdb]] debugging—with optimization, line numbers may not map correctly to source, and variables may be optimized away. ```bash gcc -O0 -g -o app app.c # debug build: no optimization, symbols kept ``` `-O1` enables basic optimizations: dead code elimination, simple inlining, register allocation. Minimal overhead; rarely used explicitly. `-O2` enables most optimizations that don't significantly increase code size: loop unrolling, function inlining, instruction scheduling, strength reduction. Good balance between speed and compile time. Recommended for production builds if not using `-O3`. ```bash gcc -O2 -o app app.c # standard production build ``` `-O3` adds aggressive optimizations: aggressive inlining, loop vectorization, loop unrolling at larger scales, speculative optimizations. Code is often larger and compilation is slower, but runtime can be 10-30% faster than `-O2` on compute-heavy code. Common in HPC. ```bash gcc -O3 -march=native -o app app.c # high-performance build ``` `-Os` optimizes for code size instead of speed. Useful for embedded systems where memory is constrained. Typically slower than `-O2`. `-Ofast` enables `-O3` plus optimizations that violate strict language semantics—most importantly, it relaxes IEEE floating-point strictness (allows fused multiply-add, reordering of operations). Can break numerically sensitive code that depends on exact rounding behavior. Use only if you've verified correctness or don't care about tiny floating-point differences. ```bash gcc -Ofast -o app app.c # aggressive, may affect numerical precision ``` **Profile-guided optimization (PGO):** Compile with `-fprofile-generate`, run on representative input, then recompile with `-fprofile-use` to optimize based on actual execution patterns: ```bash gcc -O3 -fprofile-generate -o app_profile app.c ./app_profile < test_input gcc -O3 -fprofile-use -o app app.c ``` The second compile uses profiling data to make smarter optimization decisions (inlining hot functions, branch prediction, etc.). **Benchmarking:** Always compare `-O2` and `-O3` on your actual workload. Sometimes `-O3` is faster; sometimes code bloat causes cache misses and it's slower. For HPC, measure both.