Self-Assessment Quiz: Why Fortran Is Fast
Twenty questions to confirm you can explain, not just recite, why Fortran runs fast. Aim for 16 or more. Answers and a topic map are at the end — take the whole quiz first.
Question 1
Which gfortran optimization level first turns on automatic vectorization?
- A. -O0
- B. -O1
- C. -O2
- D. -O3
Question 2
-Ofast differs from -O3 mainly because it adds -ffast-math, which:
- A. Makes the compiler use more memory
- B. Lets the compiler reorder floating-point arithmetic (treating it as associative), which can change results
- C. Disables vectorization
- D. Forces double precision everywhere
Question 3
"Inlining" is the optimization that: - A. Removes all loops from a program - B. Replaces a procedure call with a copy of the procedure's body in the caller - C. Converts a program to a single line - D. Stores arrays in the CPU registers permanently
Question 4
On typical hardware, a cache line is 64 bytes, which holds how many real(dp) values?
- A. 1
- B. 4
- C. 8
- D. 64
Question 5
For a Fortran 2D array, the cache-friendly loop nest puts the inner loop over: - A. The last index - B. The first index - C. Either index — it makes no difference - D. The largest index
Question 6
A numerical loop described as "memory-bound" is one whose speed is limited primarily by: - A. How many floating-point operations it performs - B. How fast data can be moved between memory and the CPU - C. The number of variables declared - D. The length of the source file
Question 7
The "10× loop-order difference" quoted in this book should be understood as: - A. A guaranteed, exact factor on all hardware - B. An illustrative order of magnitude whose true value you measure yourself - C. A benchmark the authors ran and recorded - D. The speedup from adding more RAM
Question 8
The no-aliasing advantage refers to the fact that Fortran: - A. Never uses pointers - B. Forbids a procedure's written argument from overlapping a read argument, letting the compiler assume distinctness - C. Automatically parallelizes every loop - D. Stores arrays in row-major order
Question 9
C's restrict keyword exists in order to:
- A. Make pointers read-only
- B. Let the C programmer promise, per pointer, the non-aliasing that Fortran guarantees by default
- C. Restrict which functions can be called
- D. Force the compiler to use -O0
Question 10
Aliasing a written dummy argument with a read one in Fortran is: - A. A guaranteed compile-time error - B. Always safe - C. Undefined behavior — the programmer's responsibility to avoid - D. Automatically fixed by the compiler
Question 11
A pure procedure helps the optimizer because it promises to:
- A. Run faster than other procedures
- B. Have no side effects, so calls may be hoisted, shared, reordered, or removed
- C. Use only integer arithmetic
- D. Always be inlined
Question 12
An elemental procedure is one that:
- A. Cannot be called on arrays
- B. Is written for scalar arguments but may be applied elementwise to whole arrays, and is automatically pure
- C. Is the fastest kind of subroutine by definition
- D. Must be recursive
Question 13
The gfortran flag -fopt-info-vec-missed reports:
- A. Every optimization the compiler applied
- B. Loops that were not vectorized, with the reason
- C. Memory leaks
- D. Compile time per function
Question 14
On Compiler Explorer, an instruction named vaddpd (versus addsd) tells you the loop was:
- A. Not compiled
- B. Vectorized — "packed double" operates on several values at once
- C. Running on the GPU
- D. Written in assembly by hand
Question 15
Why can c = a + b (whole-array) be better information for the compiler than a hand-written element loop?
- A. It uses less memory
- B. It states the whole operation over known-shape, guaranteed-distinct arrays — exactly what vectorization needs
- C. It is always run in parallel
- D. It avoids floating-point arithmetic
Question 16
Does gfortran unroll your loops automatically at -O3?
- A. Yes, always
- B. No — you request unrolling explicitly with -funroll-loops (or via profile-guided optimization)
- C. Only for integer loops
- D. Only with -O0
Question 17
True or false: "Fortran is fast because it is a low-level language, close to the hardware like assembly."
Question 18
The Project Checkpoint compares the two loop orders' output with maxval(abs(fi - fj)) and gets exactly
zero. This is legitimate (despite Chapter 20's warning about float equality) because:
- A. Fortran rounds all differences to zero
- B. The two orders perform identical arithmetic per element, so the results are bit-for-bit equal
- C. maxval ignores small differences
- D. The arrays are integers
Question 19
Which is the correct professional order of operations for making code fast?
- A. Optimize first, then measure to confirm
- B. Measure/profile to find the real bottleneck, then optimize it, then measure again
- C. Rewrite in assembly immediately
- D. Turn on -Ofast and stop thinking
Question 20
What does this print?
real(dp) :: x(4) = [1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp], y(4) = [0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp]
call axpy(2.0_dp, x, y) ! pure subroutine: y = y + alpha*x
print '(4f6.1)', y
- A.
1.0 2.0 3.0 4.0 - B.
2.0 4.0 6.0 8.0 - C.
0.0 0.0 0.0 0.0 - D.
3.0 6.0 9.0 12.0
Answer Key
| Q | Ans | Why |
|---|---|---|
| 1 | D | Auto-vectorization turns on at -O3. |
| 2 | B | -ffast-math reorders FP arithmetic (assumes associativity) and can change results. |
| 3 | B | Inlining pastes the callee's body into the caller, removing call overhead and enabling further optimization. |
| 4 | C | 64 bytes / 8 bytes per real(dp) = 8 values. |
| 5 | B | Fortran is column-major; the first index varies fastest, so it belongs innermost. |
| 6 | B | Memory-bound = limited by memory traffic, not arithmetic. |
| 7 | B | All timings here are illustrative orders of magnitude; you measure the real one. |
| 8 | B | The standard forbids aliasing a written argument with a read one. |
| 9 | B | restrict is C's opt-in promise of the non-aliasing Fortran guarantees by default. |
| 10 | C | It is undefined behavior, not a diagnosed error; the programmer must avoid it. |
| 11 | B | No side effects → the compiler may hoist, share, reorder, or delete calls. |
| 12 | B | Scalar body, array-ready, automatically pure. |
| 13 | B | It reports loops that failed to vectorize, with the reason. |
| 14 | B | …pd = packed double = vectorized; …sd = scalar double. |
| 15 | B | It exposes structure (shape, distinctness) the compiler needs to vectorize. |
| 16 | B | gfortran unrolls only when asked (-funroll-loops) or under PGO. |
| 17 | False | Fortran is high-level; its speed comes from letting the compiler optimize array code freely. |
| 18 | B | Identical per-element arithmetic → bit-for-bit identical results, so the difference is exactly 0. |
| 19 | B | Measure first, optimize the real bottleneck, then measure again (Chapter 28's ethic). |
| 20 | B | y = 0 + 2*[1,2,3,4] = [2,4,6,8]. |
Topics to review by question
- Q1–3, 16 → §27.1 (what the compiler does; optimization levels; inlining; unrolling).
- Q4–7, 15 → §27.2 (cache line, memory-bound, column-major loop order, the illustrative 10×).
- Q8–10, 17 → §27.3 (the no-aliasing advantage;
restrict; undefined behavior). - Q11–12, 20 → §27.4 (
pure/elementalas optimization licenses). - Q13–14 → §27.5 (reading the optimization report; packed vs scalar assembly).
- Q18 → Project Checkpoint + Chapter 20 (bit-identical arithmetic).
- Q19 → §27.5 and the bridge to Chapter 28 (measure first).
Scored below 16? The two sections to reread are §27.2 (loop order) and §27.3 (no aliasing) — they are the load-bearing ideas the rest of Part VII is built on.