Skip to content

Parallel Builds

Scope: -j, what it does, and the one rule that makes it safe.

The flag

By default Make runs one recipe at a time. -jN runs up to N in parallel:

$ make -j4

On a multi-core machine, a large build with -j$(nproc) finishes dramatically faster — compiling fifty files uses fifty cores instead of one. (Omit the number — bare -j — and Make runs everything at once, which can exhaust memory on big projects. Give it a number.)

The one rule

Parallel Make is safe iff your prerequisites are honest. Make parallelizes everything it believes is independent. If link really needs gen-header to finish first but the Makefile doesn't say so, -j will run them together and fail intermittently — the worst kind of bug, because serial make works fine.

# honest: link waits for the header
program: main.o gen-header.h
    $(CC) -o $@ main.o

gen-header.h: gen.py
    python3 gen.py > $@

The discipline from How Make Decides — list every real input — is what makes -j trustworthy. If -j breaks a build that works serially, the Makefile is lying about some dependency. That's a bug worth fixing anyway.

What doesn't parallelize

Recipes within a single rule always run in order, one after another — -j parallelizes rules, not lines. And some recipes can't run concurrently at all (two rules writing the same file). For those, .NOTPARALLEL disables parallelism for the whole Makefile, or order dependencies (|) serialize specific pairs.

Check: make -j4 --trace shows the interleaved order Make chose. If two recipes that must be ordered ran together, a prerequisite is missing.