What Optimization Levels Actually Change

The same code.
Different machine instructions.
Different behavior under the debugger.

1. The Purpose of Optimization

Compilers translate high-level code into machine instructions. During this process, they apply optimization passes to improve performance, reduce code size, or simplify execution.

Optimization levels control how aggressively the compiler transforms the program. These levels determine whether the compiler prioritizes easier debugging, smaller binaries, or faster execution.

2. No Optimization -O0

-O0 disables most optimization passes.

Characteristics:
  • Code closely follows the original source
  • Variables remain visible in the debugger
  • Execution order matches the written code

This level is ideal for debugging and early development. However, it produces slow and inefficient machine code, making it unsuitable for performance testing or final firmware builds.

3. Basic Optimization -O1

-O1 enables simple optimizations that improve efficiency without drastically altering program structure.

Typical transformations include:
  • removing redundant instructions
  • simple constant propagation
  • basic dead code elimination

The resulting program runs faster than -O0, while still remaining relatively easy to debug.

4. Production Optimization -O2

-O2 enables a much broader set of optimization techniques, such as:
  • function inlining
  • loop optimization
  • instruction scheduling
  • better register allocation

For most firmware projects, -O2 is considered the standard optimization level for production builds because it balances performance and binary size.

5. Aggressive Optimization -O3

-O3 applies even more aggressive transformations focused on maximum execution speed, including:
  • deeper function inlining
  • loop unrolling
  • vectorization where supported
  • advanced instruction scheduling

While this can improve performance, it may also increase binary size and sometimes introduce unexpected timing differences, which can be critical in embedded systems.

6. Why This Matters in Embedded Systems

In firmware development, optimization levels influence more than just speed. They can affect:
  • interrupt timing
  • memory layout
  • instruction ordering
  • debugging behavior

A bug that does not appear at -O0 may appear at -O2 or -O3 because the compiler reorganizes the program. For this reason, firmware should always be tested using the same optimization level used in production builds.
Optimization does not change your code.
It changes what the processor actually runs.