开发者

GNU make's -j option

Ever since I learned about -j I've used -j8 blithely. The other day I was compiling an atlas installation and the make failed. Eventually I tracked it down to things being made out of order - and it worked fine once I went back to singlethreaded make. This开发者_StackOverflow中文版 makes me nervous. What sort of conditions do I need to watch for when writing my own make files to avoid doing something unexpected with make -j?


I think make -j will respect the dependencies you specify in your Makefile; i.e. if you specify that objA depends on objB and objC, then make won't start working on objA until objB and objC are complete.

Most likely your Makefile isn't specifying the necessary order of operations strictly enough, and it's just luck that it happens to work for you in the single-threaded case.


In short - make sure that your dependencies are correct and complete.

If you are using a single threaded make then you can be blindly ignoring implicit dependencies between targets. When using parallel make you can't rely on the implicit dependencies. They should all be made explicit. This is probably the most common trap. Particularly if using .phony targets as dependencies.

This link is a good primer on some of the issues with parallel make.


Here's an example of a problem that I ran into when I started using parallel builds. I have a target called "fresh" that I use to rebuild the target from scratch (a "fresh" build). In the past, I coded the "fresh" target by simply indicating "clean" and then "build" as dependencies.

build: ## builds the default target
clean: ## removes generated files
fresh: clean build ## works for -j1 but fails for -j2

That worked fine until I started using parallel builds, but with parallel builds, it attempts to do both "clean" and "build" simultaneously. So I changed the definition of "fresh" as follows in order to guarantee the correct order of operations.

fresh:
    $(MAKE) clean
    $(MAKE) build

This is fundamentally just a matter of specifying dependencies correctly. The trick is that parallel builds are more strict about this than are single-threaded builds. My example demonstrates that a list of dependencies for given target does not necessarily indicate the order of execution.


If you have a recursive make, things can break pretty easily. If you're not doing a recursive make, then as long as your dependencies are correct and complete, you shouldn't run into any problems (save for a bug in make). See Recursive Make Considered Harmful for a much more thorough description of the problems with recursive make.


It is a good idea to have an automated test to test the -j option of ALL the make files. Even the best developers have problems with the -j option of make. The most common issues is the simplest.

myrule: subrule1 subrule2
     echo done

subrule1:
     echo hello

subrule2:
     echo world

In normal make, you will see hello -> world -> done. With make -j 4, you will might see world -> hello -> done

Where I have see this happen most is with the creation of output directories. For example:

build: $(DIRS) $(OBJECTS)
     echo done

$(DIRS):
     -@mkdir -p $@

$(OBJECTS):
     $(CC) ...


Just thought I would add to subsetbrew's answer as it does not show the effect clearly. However adding some sleep commands does. Well it works on linux.

Then running make shows differences with:

  • make
  • make -j4

all: toprule1

toprule1: botrule2 subrule1 subrule2
    @echo toprule 1 start
    @sleep 0.01
    @echo toprule 1 done

subrule1: botrule1
    @echo subrule 1 start
    @sleep 0.08
    @echo subrule 1 done

subrule2: botrule1
    @echo subrule 2 start
    @sleep 0.05
    @echo subrule 2 done

botrule1:
    @echo botrule 1 start
    @sleep 0.20
    @echo "botrule 1 done (good prerequiste in sub)"

botrule2:
    @echo "botrule 2 start"
    @sleep 0.30
    @echo "botrule 2 done (bad prerequiste in top)"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜