lowlatency · medium
Compiler Optimization Levels: -O0 → -O3 (and -Og / -Os / -Ofast)
Compiler optimization levels are a single dial trading compile speed and debuggability for run speed, all under the 'as-if' rule (observable behavior is preserved — except -Ofast). -O0 is the default: no optimization, code maps 1:1 to source, best for gdb, far too slow to ship. -O1/-O2 turn on inlining, common-subexpression elimination, constant folding, dead-code elimination, loop optimization and register allocation; -O2 is the release default and pairs with -g for optimized builds that still produce real backtraces. -O3 adds auto-vectorization (SIMD), aggressive inlining and loop unrolling — sometimes much faster, sometimes slower due to code bloat and i-cache misses, so it must be measured, and -march=native is needed to actually emit AVX. -Og optimizes while staying debuggable (the dev-build default); -Os optimizes for size (clang -Oz smaller still); -Ofast is -O3 plus -ffast-math, which reassociates floating-point math and assumes no NaN/Inf, breaking strict IEEE 754 — never use it where exact float results matter. Choose by profiling: -O0/-Og while developing, -O2 to ship, -O3 -march=native only on a profiled hot path that benchmarks faster, -Os when size dominates.
Optimization is one dial: -O0 (no opt, best debugging) → -O2 (the release default: inlining, CSE, loop opts) → -O3 (aggressive: auto-vectorization, unrolling — NOT always faster, measure it). -Og = debuggable dev builds, -Os = size, -Ofast = -O3 + fast-math (breaks IEEE). Ship -O2; reach for -O3 -march=native only on a profiled hot path.
The code
g++ main.cpp # -O0 by DEFAULT: no optimizationg++ -O0 -g main.cpp # debugging: code maps 1:1 to sourceg++ -O2 main.cpp # the release default: fast + saneg++ -O3 -march=native main.cpp # aggressive: vectorize, unroll, inline hardg++ -Og -g main.cpp # optimized BUT debuggable (dev builds)g++ -Os main.cpp # optimize for SIZE (embedded / i-cache)g++ -Ofast main.cpp # -O3 + -ffast-math: BREAKS strict IEEEWhat this lesson walks through
- 01Optimization is a single dial
- 02-O0 — what you wrote is what you get
- 03-O1 / -O2 — the production sweet spot
- 04-O3 — aggressive, and not always faster
- 05The siblings — -Og, -Os/-Oz, -Ofast
- 06How to choose a level
g++ and clang expose optimization as one dial: -O0, -O1, -O2, -O3 (plus -Og, -Os, -Ofast). Turn it up and the compiler works harder — the code runs faster but compiles slower and gets harder to debug, because the machine code stops matching your source line for line. Crucially, every level obeys the 'as-if' rule: optimizations must preserve your program's observable behavior — the one exception is -Ofast, where you explicitly opt out.
See it animated — step by step, at your own pace
Unlock the full interactive walkthrough of Compiler Optimization Levels: -O0 → -O3 (and -Og / -Os / -Ofast) and 100+ animated C++ interview lessons.