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.