Chapter 30 — Key Takeaways (Compiler Flags and Platform-Specific Optimization)
A one-page field guide to the flags that buy speed — and the ones that quietly change your answer.
The gfortran optimization ladder
| Flag | What it does | Result-preserving? |
|---|---|---|
-O0 |
No optimization (the default). Fast compiles, honest debugging. | Yes |
-O1 |
Cheap, always-safe optimizations. | Yes |
-O2 |
Strong, well-tested suite. The release default. | Yes |
-O3 |
Aggressive: more inlining, loop transforms, vectorization. Occasionally slower — measure. | Yes |
-Ofast |
-O3 + -ffast-math (+ -fno-protect-parens). Relaxes IEEE; can change results. |
NO |
-march=native |
Compile for this CPU's instruction set (widest SIMD). Not portable to other CPUs. | Yes (but may enable FMA → last-bit changes) |
-flto |
Link-time optimization: inline/analyze across files. Pass at compile and link. | Yes |
The one rule to memorize: -O0 through -O3 change speed, not answers. Only -Ofast/-ffast-math
can change the number your program prints.
The two builds (never one)
| Flags | Use it for | |
|---|---|---|
| Development | -g -O0 -fcheck=all -fbacktrace -ffpe-trap=invalid,zero,overflow |
Writing and debugging. Catches bounds/alloc/NaN errors. |
| Release | -O3 -march=native -flto (or a safe -O2) |
Real runs and all timing you report. Checks removed. |
Never time a
-fcheck=allbuild — the run-time checks can slow it several-fold, so you measure the checks, not your algorithm. Remove-fcheck=allfor production.
Cross-compiler flag translation
| Idea | gfortran | Intel ifx/ifort |
NVIDIA nvfortran |
|---|---|---|---|
| Aggressive optimize | -O3 |
-O3 |
-fast |
| Tune for host CPU | -march=native |
-xHost |
(part of -fast) |
| Whole-program / LTO | -flto |
-ipo |
— |
| Optimization report | -fopt-info |
-qopt-report |
-Minfo |
| IEEE-strict FP | (default) | -fp-model precise |
-Kieee |
The cross-compiler trap: Intel's default floating-point model is relaxed (
-fp-model fast), soifxcan print different numbers than gfortran for the same source. Add-fp-model precisefor agreement. (ifortis deprecated in favor of the LLVM-basedifx. Verify vendor flags against your version.)
Terms introduced
-Ofast/ relaxed IEEE —-O3plus-ffast-math; permits reassociation and ignores parentheses. Can turn(1e20 + -1e20) + 1from1.0into0.0. Validate before trusting; connects to Chapter 20.-march=native— build for the exact host CPU's instruction sets. Real speed on vectorizable code; the binary may crash (illegal instruction) on a CPU that lacks those instructions.-flto/ link-time optimization — defer optimization to link time so the whole program is visible; enables cross-file inlining. Pass it at both compile and link.- Profile-guided optimization (PGO) — instrument → run on representative input → rebuild with the profile. Helps branchy code; little help for a branch-free stencil.
- Reproducible build — one whose compiler, version, flags, architecture, and libraries are recorded so the result can be reconstructed and trusted.
Reproducibility: make the binary record itself
use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options
print '(2a)', 'compiler: ', compiler_version() ! e.g. "GCC version 13.2.0"
print '(2a)', 'options : ', compiler_options() ! the exact flags used
Record with every timing: compiler + version, the full flag list, and the machine/CPU (especially if
-march=native was used). A number without its recipe is not a measurement.
Profile-guided optimization (gfortran, three steps)
$ gfortran -O3 -fprofile-generate prog.f90 -o prog_instr # 1. instrument
$ ./prog_instr < representative_input # 2. run on REAL data (writes .gcda)
$ gfortran -O3 -fprofile-use prog.f90 -o prog_pgo # 3. rebuild using the profile
Worth it when: profiling shows a branchy hot spot, the run is long/repeated, and you can automate it. Skip it for tight numerical kernels.
Which flag / when — decision aid
| Situation | Reach for |
|---|---|
| Writing / debugging code | -g -O0 -fcheck=all -fbacktrace |
| A safe, fast release | -O2 |
| A vectorizable kernel, build == run machine | -O3 -march=native -flto |
| Squeezing branchy code, long runs | add PGO (-fprofile-generate/-use) |
"Bug appears only at -O3" |
rebuild -O0 -fcheck=all -finit-real=snan — it's your undefined behavior |
| Shipping to unknown CPUs | drop -march=native; pick a conservative baseline -march= |
| Matching results across compilers | set optimization and FP model explicitly (-fp-model precise on Intel) |
Common pitfalls
-Ofastchanges results silently — no warning; can break compensated sums and cancellation-prone code. Validate against a strict build; record that you used it.-march=nativein a shippedMakefile/fpm.toml— builds a binary that may not run elsewhere. Put it behind an opt-in release profile.- Timing a
-fcheck=allbuild — measures the safety checks, not your code (the classic false slowdown). - Comparing a debug baseline to a release build — conflates "removed checks" with "optimized." Compare checks-off vs checks-off.
- Forgetting
-fltoat the link step — it must appear at compile and link. - Assuming compilers agree by default — Intel's relaxed FP default drifts the last digits.
Numbers & rules worth memorizing
- gfortran's default with no
-Oflag is-O0(unoptimized). -fltogoes on both the compile and link commands.- A typical
-O0→-O3 -march=native -fltospeedup on a vectorizable stencil is on the order of a few × — illustrative, machine-dependent (Tier 2); always measure and record. -Ofast=-O3+-ffast-math; if you want-O3's speed without the numeric risk, use-O3alone.
Heat-solver piece added this chapter
Not code — discipline. Build the finished solver two ways (-O0 baseline, -O3 -march=native -flto
release), confirm the field checksum is unchanged (600.00 for the 5×5 two-step solve — flags buy speed,
not a different answer), measure the illustrative speedup, and write the flags into a BUILD.md beside the
result. That coupling of result + flags + machine is the seed of the reproducible project in
Chapter 37.