On this page I will try to capture interesting make hacks that I come across while working on build and GNU make. I seldom have patience and/or time to write down what I've discovered so, in many cases, what you will find below is a raw brain dump without much explanations. A lot of the stuff is GNU make-specific.

Parallel builds on NUMA machines

I have a dual AMD Opteron box and, as you may know, Opterons use so-called Non-Uniform Memory Architecture (NUMA). In short, each CPU has its own memory controller and a bank of memory attached to it. When CPU1 needs to access a memory location in the memory bank of CPU2 it has to bother CPU2's memory controller. As a result, some memory locations are "closer" (performance-wise) to CPU1 than CPU2 and vice versa. Plus Linux kernel 2.6-series has optional support for NUMA.

I decided to measure the time it takes to build a project using GNU make with -j 2 when all memory is in CPU1's bank and when it is divided equally between CPU1 and CPU2. The former measured at 126.29 seconds average while the latter measured at 121.18 seconds average which is 5.11 sec or 4.2% faster.

Last updated on 03 Oct 2005

Building executables with implicit pattern rules

Writing implicit pattern rules for building executables is tricky due to the lack of an extension that we can restrict the rule based on. The first attempt usually ends up being along these lines:

    $(CC) -o $@ $^

The problem with this rule is that make will use it to build anything for which there is no other rule, not just executables. We don't want that. The next version usually looks like this:

%: %.o
    $(CC) -o $@ $^

Now there should be an object file with the same name in order for this rule to match. Sometimes, however, this is too restrictive:

driver: foo.o bar.o baz.o

With the introduction of the second expansion in GNU make 3.81, we can do a lot better:

percent := %

%: $$(if $$(filter $$(percent).o,$$^),,%.o)
	$(CC) -o $@ $^

Later I discovered one nasty property of this rule: it will build anything that has an object file as its prerequisite. That's right, I am talking about archives and shared objects:

libfoo.so: foo.o

libfoo.a: foo.o

The solution would be to always put implicit rules that build archives and shared objects before the rule that builds executables.

Last updated on 08 Sep 2005

Copyright © 2005 Boris Kolpackov.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, version 1.2; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts.