Table of Contents

Makefile Performance

Incremental builds are Make's strength—only rebuild what changed. But slow Makefiles can still hurt productivity. Optimize with parallelization and caching.

Parallel builds with -j:

make -j4            # run up to 4 recipes in parallel
make -j              # unlimited parallelism (risky on limited systems)

-j4 runs 4 compile jobs concurrently. On a 4-core machine, this gives ~4x speedup if recipes don't have synchronization conflicts.

Load-based parallelism:

make -l 4           # run while average load < 4

-l 4 starts new jobs only if system load is below 4. Prevents overwhelming the system.

Watching parallelization:

make -j4 --print-directory

--print-directory shows which subdirectory Make is working in, helpful for debugging parallel issues.

Avoiding redundant recipes: Don't call expensive functions (like $(shell ...)) repeatedly:

# Slow: calls $(shell ...) every time
SRCS = $(shell find . -name "*.c")
 
# Fast: calls once, reuses result
SRCS := $(shell find . -name "*.c")

Use := (immediate assignment) for expensive operations so they're computed once.

Caching expensive computations:

git_version := $(shell git describe --tags 2>/dev/null || echo "unknown")
 
version.h:
	echo "#define VERSION \"$(git_version)\"" > version.h

Compute git_version once at Makefile parse time, reuse everywhere.

Reducing object files: Fewer files = fewer compilation steps:

# Many small files (slow)
app: main.o util.o helper.o string.o math.o ...
	gcc -o app $^
 
# Fewer larger files (faster)
app: core.o utils.o ...
	gcc -o app $^

Grouping source into fewer object files reduces link overhead.

Ccache for faster rebuilds: Use ccache to cache compiler output:

CC = ccache gcc
CXXFLAGS = -O3 -Wall

ccache memoizes compilation results. Recompiling the same file is instant if nothing changed.

Link-time optimization overhead: LTO (-flto) makes linking slow:

# Slow if many objects
gcc -flto -o app *.o
 
# Faster: parallelize
gcc -flto -o app *.o -j4

Use -j with GCC when using LTO for parallelized linking.

Profiling Make: See where time is spent:

make --debug=b > debug.txt 2>&1

Produces a detailed log of every Make decision. Useful for identifying bottlenecks.