Exercises: The Fortran Ecosystem

This chapter is about the world around the language, so its exercises mix three flavours: understanding (what each library and tool is for), hands-on (create an fpm project, add a dependency, decode a LAPACK name), and estimation (how much arithmetic a BLAS call really does). A few problems need a working fpm and internet access; where they do, they say so, and the appendix gives a model answer rather than a single right one.

Difficulty: ⭐ warm-up · ⭐⭐ standard · ⭐⭐⭐ deeper. Solutions: worked solutions to the daggered (†) and odd-numbered problems are in appendices/answers-to-selected.md; the compilable ones are in code/exercise-solutions.f90. Try every problem before you look.

A note on running code. Some problems ask you to build or run something with fpm — do it! The book's own no-execution policy is a discipline for the author, not a rule for you. Compiling and running is exactly how you learn; predict the output first, then check.


Part A — Name the Pieces ⭐

16.1 † In one sentence each, say what these are for: LAPACK, BLAS, fpm, stdlib, FORD, pFUnit.

16.2 Decode each LAPACK routine name into precision · matrix-type · operation: (a) dgesv, (b) sgetrf, (c) zheev, (d) dgesvd. (For (c), he = Hermitian.)

16.3 † What are the three BLAS levels, and what kind of object does each operate on? Which level does a matrix-matrix multiply belong to?

16.4 Name the three directories an fpm new project creates for source, and say what goes in each.

16.5 Which three fpm commands build, run, and test a project?

16.6 † For each task, name the library or tool you would reach for: (a) solve $A\mathbf{x}=\mathbf{b}$; (b) take a fast Fourier transform; (c) write 50 GB of gridded output another lab can read; (d) run across 1000 cluster nodes; (e) autocomplete and jump-to-definition in your editor; (f) generate HTML docs.


Part B — LAPACK and BLAS, Understood ⭐⭐

16.7 † Explain the "separation of concerns" that makes LAPACK both portable and fast. Who writes the portable part, and who writes the fast part?

16.8 The reference BLAS from Netlib is correct but slow; OpenBLAS or MKL can be many times faster on the same hardware. Since they compute the same answer, where does the speed difference come from?

16.9 † Why did LAPACK's designers restructure the old LINPACK/EISPACK algorithms to spend as much time as possible inside Level-3 BLAS calls? (Hint: arithmetic intensity — flops per byte moved.)

16.10 True or false, with one sentence of justification: "Because matmul is a Fortran intrinsic, it is always at least as fast as calling a tuned BLAS dgemm."

16.11 † A colleague says "NumPy is faster than Fortran, so why bother?" Using this chapter, give the one-sentence correction that names what NumPy is actually calling.


Part C — fpm Hands-On ⭐⭐

These want you at a terminal. If you do not have fpm yet, install it (see fortran-lang.org) or reason through the expected result.

16.12 Run fpm new demo16. List the files and directories it created, and identify which file becomes the executable when you run fpm run.

16.13 † (Find the bug.) This fpm.toml fails to fetch its dependency. What is wrong, and what is the one-character-class fix?

[dependencies]
stdlib = "https://github.com/fortran-lang/stdlib"

16.14 Add a module src/greet.f90 with a function greeting() that returns a string, use it from app/main.f90, and fpm run. You never edited a build script or told fpm about the new file. What did fpm work out on your behalf, and which Chapter 8 fact made that necessary?

16.15 † Explain the difference between these two dependency specifications and when you would use each:

stdlib = { git = "https://github.com/fortran-lang/stdlib" }
stdlib = { git = "https://github.com/fortran-lang/stdlib", tag = "v0.7.0" }

Part D — stdlib and the Community ⭐⭐

16.16 Rewrite this hand-rolled snippet using the stdlib call it replaces (you do not need to compile it — just show the two lines and name the module):

mu = sum(x) / real(size(x), dp)   ! the mean of x

16.17 † A program that begins with use stdlib_stats, only: mean will not compile with a bare gfortran file.f90 command, but the same program builds fine under fpm build. Explain precisely why.

16.18 Visit fortran-lang.org (or recall from the chapter) and name three things the community currently maintains, with one sentence each. (Preview from Chapter 1's exercise, now that you know them.)

16.19 † What is LFortran, what unusual thing can it do that gfortran cannot, and why does the chapter advise using an established compiler (not LFortran) to build production code today? (We return to it in Chapter 39.)


Part E — Back of the Envelope ⭐⭐⭐

Order-of-magnitude estimates. Show your reasoning; the compilable ones are in code/exercise-solutions.f90.

16.20 † A general $n \times n$ matrix multiply (a Level-3 BLAS gemm) does about $2n^3$ floating-point operations. Estimate the flop count for $n = 2000$. If a tuned BLAS sustains an illustrative $40$ Gflop/s, how long does the multiply take? Compare to a reference BLAS at, say, $2$ Gflop/s.

16.21 Arithmetic intensity. A matrix-matrix multiply moves $O(n^2)$ numbers and does $O(n^3)$ flops; a vector operation like $y \leftarrow y + a x$ moves $O(n)$ numbers and does $O(n)$ flops. Compute the flops per number moved for each (as a function of $n$), and explain in one sentence why the matrix-matrix operation can run near the processor's peak while the vector one cannot.

16.22 † Your heat field is a double-precision array of $1000 \times 1000$ cells, and your solver keeps three such arrays (current, next, and a scratch). Estimate the memory footprint in megabytes. (Recall real(dp) is 8 bytes; $1\,\text{MB} = 10^6$ bytes for this estimate.)

16.23 You currently rebuild a 300-file project with a hand-written Makefile that you update by hand each time a use dependency changes; you estimate this costs you 20 minutes a week in build-file maintenance. Converting to fpm takes an afternoon (4 hours). After how many weeks does the switch pay for itself?


Part F — Port It and Find the Bug ⭐⭐

16.24 † (Port it.) Port this Python to Fortran using only intrinsics (no stdlib), then say which stdlib call would replace your whole body in a real project:

import numpy as np
def popstd(y):
    return np.std(y)          # population standard deviation

Test it on [1, 2, 3, 4, 5] (the answer is $\sqrt{2} \approx 1.41$).

16.25 (Find the bug.) A build against NetCDF fails at link time with undefined reference to nf90_open, even though the developer insists "NetCDF is definitely installed." What is the most likely cause, given §16.2?

16.26 † (Find the bug.) This link command for a LAPACK program fails to resolve the LAPACK symbols on some systems. What ordering rule is being violated?

$ gfortran -llapack -lblas solve.f90 -o solve

16.27 (Find the bug.) A teammate documents a function for FORD but the generated page shows no description for the argument. What is wrong here, and what is the fix?

pure function scale_it(x, a) result(y)
  real(dp), intent(in) :: x(:)   ! the input vector
  real(dp), intent(in) :: a      ! the scale factor
  real(dp) :: y(size(x))
  y = a * x
end function scale_it

Part G — Design It and Interleave ⭐⭐/⭐⭐⭐

16.28 † (Design it — extend the solver.) Sketch the fpm project layout for the heat solver: name the files you would place in app/, src/, and test/, given the modules you built through Chapter 13 (kinds, heat_solver, heat_io, the field_t type, the driver). Write the [dependencies] section that pulls in stdlib. You need not compile it — produce the tree and the manifest.

16.29 (Modernize the build.) You inherit a project built by this shell script:

$ gfortran -c kinds.f90
$ gfortran -c heat_solver.f90
$ gfortran -c heat_io.f90
$ gfortran main.f90 kinds.o heat_solver.o heat_io.o -o heat

Describe, in a few sentences, what converting this to fpm would eliminate, and why the order of those first three -c commands is a maintenance hazard that fpm removes. (Interleaves Chapter 8.)

16.30 † (Interleave — Chapters 5, 8, 13.) For each, name the earlier-chapter idea the ecosystem builds on: (a) fpm compiles your modules in dependency order — which Chapter 8 mechanism forces that order? (b) matmul and a BLAS gemm both rely on which Chapter 5 memory-layout fact to be fast? (c) When fpm test catches a run that produced wrong numbers, which Chapter 13 statement should the failing program have used to exit with a non-zero status?


Solutions to the daggered and odd-numbered problems are in appendices/answers-to-selected.md; the calculations behind 16.20 and 16.24 are in code/exercise-solutions.f90. The hands-on fpm problems (16.12, 16.14, 16.18) have answers that depend on your setup and the day you look, so the appendix gives a model response.