74

I have a Makefile on a machine that has a ton of cores in it, but I always seem to forget to write -jX when compiling my project and it takes way longer than it should.

Is there some way I can set the -j flag through an environment variable or some other persistent config file so that make will automatically execute multiple jobs in parallel on this machine?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
KarateSnowMachine
  • 1,490
  • 2
  • 13
  • 17
  • Related: http://stackoverflow.com/questions/2527496/how-can-i-write-a-makefile-to-auto-detect-and-parallelize-the-build-with-gnu-make – Jack Kelly Jan 24 '11 at 05:50
  • @sanmai, which OS do you need it for? And why doesn't `alias make='make -j$(getconf _NPROCESSORS_ONLN)'` work for you? – Tarun Lalwani Feb 08 '18 at 09:14
  • Alias won't suffice because I need to publish the Makefile and have it still working. I need this for GNU Make 4.1. @TarunLalwani – sanmai Feb 09 '18 at 03:37
  • Alias is not safe because *many* developers have made nondeterministic `Makefile`s in run with multiple CPU cores. As a result, only files marked as compatible with parallel execution should get `-j` flag (and maybe `-l` and `-s` flags, too) by default. – Mikko Rantalainen Aug 16 '22 at 13:38

10 Answers10

45

I'm assuming you're using Linux. This is from my ~/.bashrc

# parallel make
export NUMCPUS=`grep -c '^processor' /proc/cpuinfo`
alias pmake='time nice make -j$NUMCPUS --load-average=$NUMCPUS'

sample usage

samm@host src> echo $NUMCPUS
8
samm@host src> pmake

becomes time nice make -j8 --load-average=8.

To answer your specific question about putting this into a Makefile, I don't find it practical to sprinkle this logic all over my Makefiles. Putting this into a top level Makefile also isn't a great solution since I often build from sub-directories and wish to build them in parallel as well. However, if you have a fairly flat source hierarchy, it may work for you.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
39

It appears that the MAKEFLAGS environment variable can pass flags that are part of every make run (at least for GNU make). I haven't had much luck with this myself, but it might be possible to use -l rather than -j to automatically run as many jobs as are appropriate for the number of cores you have available.

Jeremiah Willcock
  • 30,161
  • 7
  • 76
  • 78
  • 1
    Well, you can pass `-l` or `-j` but with no arguments. Try for yourself: https://gist.github.com/sanmai/dcc31ae20afa6e8ba4721f174fe05fd9 – sanmai Feb 01 '18 at 09:03
  • Note that passing `-l` disables load limiting features whereas `-j` with no arguments tell `make` to start unlimited number of paraller processes. – Mikko Rantalainen Apr 25 '19 at 08:16
36

As Jeremiah Willcock said, use MAKEFLAGS, but here is how to do it:

export MAKEFLAGS="-j $(grep -c ^processor /proc/cpuinfo)"

or you could just set a fixed value like this:

export MAKEFLAGS="-j 8"

If you really want to boost performance you should use ccache by adding something like the following to your Makefile:

CCACHE_EXISTS := $(shell ccache -V)
ifdef CCACHE_EXISTS
    CC := ccache $(CC)
    CXX := ccache $(CXX)
endif
jcoffland
  • 5,238
  • 38
  • 43
  • Won't work for GNU Make 4.1 - [see example here](https://gist.github.com/sanmai/dcc31ae20afa6e8ba4721f174fe05fd9) – sanmai Feb 01 '18 at 09:05
  • @sanmai, I'm using MAKEFLAGS here not MAKEOPTS as used in your example. I believe GNU Make 4.1 still has MAKEFLAGS. – jcoffland Feb 09 '18 at 19:31
  • Nope. See for yourself: `strings /usr/bin/make | grep MAKEFLAGS` – sanmai Feb 10 '18 at 02:22
  • @sanmai `MAKEFLAGS` works for me in GNU Make 4.1 on Slackware, perhaps it was disabled by whomever compiled it for you?: `$ make --version` `GNU Make 4.1 Built for x86_64-slackware-linux-gnu` (`$ strings /usr/bin/make | grep MAKEFLAGS` returns `GNUMAKEFLAGS`) – TimP Aug 18 '20 at 03:07
  • 1
    @TimP yes, apparently it's `GNUMAKEFLAGS` now – sanmai Aug 20 '20 at 05:55
  • `export MAKEFLAGS="-j $(nproc)"` – Steven Lu Aug 19 '22 at 19:36
34

I usually do this as follows in my bash scripts:

make -j$(nproc)
Bl00dh0und
  • 674
  • 7
  • 6
6

Aliases are not expanded inside scripts. It's better to create a separate make script and place it into one of the $PATH directories:

#!/bin/sh

if [ -f /proc/cpuinfo ]; then
    CPUS=`grep processor /proc/cpuinfo | wc -l`
else
    CPUS=1
fi
/usr/bin/make -j`expr $CPUS + 1` "$@"
Community
  • 1
  • 1
svlasov
  • 9,923
  • 2
  • 38
  • 39
6

You can add a line to your Makefile similar to the following:

NUMJOBS=${NUMJOBS:-" -j4 "}

Then add a ${NUMJOBS} line in your rules, or add it into another Makefile var (like MAKEFLAGS). This will use the NUMJOBS envvar, if it exists; if it doesn't, automatically use -j4. You can tune or rename it to your taste.

(N.B.: Personally, I'd prefer the default to be -j1 or "", especially if it's going to be distributed to others, because although I have multiple cores also, I compile on many different platforms, and often forget to dis-able the -jX setting.)

Sdaz MacSkibbons
  • 27,668
  • 7
  • 32
  • 34
  • Thanks for this answer. I went with Jeremiah's answer because it's a little easier to understand and I don't have to modify the makefile at all (letting people downloading the project set whatever -j flag they want however they want). Informative nonetheless. – KarateSnowMachine Jan 24 '11 at 04:00
  • As far as I can tell, adding -jX to MAKEFLAGS has stopped working on the latest release of GNU make sometime in the last year. Has anyone else experienced this? – golvok Jun 10 '17 at 20:37
4

Until sometime in 2016, you could put this in your makefile: (GNU make tested)

MAKEFLAGS += "-j$(NUM_CORES) -l$(NUM_CORES)

(where NUM_PPROCS is calculated or set according to one of many of the other answers here) And, bam! you have multi-process building going on.

Given that this has stopped working, the best thing that I could come up with is this, where the makefile calls itself, but with -jX and -lX.

ifeq ($(PARALELL_WRAPPER_ABXCOEOEKCOEBMQJKHTOEUB),done)

all: ...
   ...

other_target: ...
    ...

else
# add parallelism equal to number of cores every time.
# "random" strings are to ensure uniqueness
NUM_CORES ?= $(shell grep -c "vendor_id" /proc/cpuinfo)
MAKEFLAGS +=" -j$(NUM_CORES) -l$(NUM_CORES) "

# for the default target case
parallel_wrapper_default_target_anthsqjkshbeohcbmeuthnoethoaeou:
    $(MAKE) PARALELL_WRAPPER_ABXCOEOEKCOEBMQJKHTOEUB=done

# catches everything else
% :
    $(MAKE) $@ PARALELL_WRAPPER_ABXCOEOEKCOEBMQJKHTOEUB=done

endif
golvok
  • 1,015
  • 12
  • 25
2

On Ubuntu 16.4 using all CPU cores:

export MAKEFLAGS='-j$(nproc)'

or

export MAKEFLAGS='-j 2'
mwweb
  • 7,625
  • 4
  • 19
  • 24
1

At the beginning of a Makefile:

MAKEFLAGS+="j"

It won't take numeric arguments for any version of GNU Make before 4.2. After 4.2 you can do:

MAKEFLAGS+="j2"

For versions earlier than 4.2 if you happen to have some jobs running out of memory, you could make them run only one at a time with flock from util-linux-ng. For example, a convert utility from ImageMagick will use all resources it can get when used to optimize images according to Google's recommendations, so there's no point to have it run in parallel.

%.min.jpg: %.jpg
    @flock --wait 600 Makefile convert $< -sampling-factor 4:2:0 -strip $@

It is important to set a long wait time because make will still run most of these commands in parallel. Therefore, wait time must be as such as the deepest queue execution time. If you have eight cores, and eight large images take a minute to optimize, you must set the wait time to at least a minute.

With hundreds of cores and huge images, six hundred seconds set above might not be enough.

sanmai
  • 29,083
  • 12
  • 64
  • 76
  • The way I interpret that changelog is that now it is possible to tell what the parallelism is from within a Makefile by inspecting MAKEFLAGS. GNU Make uses a job token based system, so the actual parallelism is whatever number of tokens the job server is willing to give out. Also, the answers here that use MAKEFLAGS do actually work, though I too had noticed that MAKEFLAGS used to not have the number. – golvok Nov 08 '18 at 19:21
  • @golvok They do actually work as explained since 4.2, [just like shown here](https://gist.github.com/sanmai/283006cae742aa8e0b12b71748391465). – sanmai Nov 09 '18 at 00:16
-2

I would just put

alias make='make -j'

in ~/.profile or ~/.bashrc.

According to the manual:

If there is nothing looking like an integer after the ‘-j’ option, there is no limit on the number of job slots.

Alberto
  • 67
  • 5
  • 11
    On my 2 core machine an unqualified `-j` quickly eats up all my memory on a big build. Not recommended. – ntc2 May 16 '14 at 04:00
  • Yeah, this seems like something that would cause a fork bomb on your computer with a large Makefile. – gib Apr 01 '17 at 17:39
  • `-j` alone will scale to unlimited processes which is obviously insane. You actually want `CPUS=$(nproc)` and `export MAKEFLAGS="-j$CPUS -l$CPUS"` instead. – Mikko Rantalainen Aug 16 '22 at 13:45