One target in my makefile is a very CPU and time consuming task. But I can split the workload and run the task several times in parallel to speed up the entire process.
My problem is that make doesn't wait for all processes to complete.
Consider this simple script, named myTask.sh
:
#!/bin/bash
echo "Sleeping $1 seconds"
sleep $1
echo "$1 are over!"
Now, let's call this from a bash script, and use wait
to wait for all tasks to complete:
#!/bin/bash
echo "START"
./myTask.sh 5 &
./myTask.sh 15 &
./myTask.sh 10 &
wait # Wait for all tasks to complete
echo "DONE"
The output is as expected:
START
Sleeping 15 seconds
Sleeping 5 seconds
Sleeping 10 seconds
5 are over!
10 are over!
15 are over!
DONE
But when trying the same in a Makefile
:
test:
echo "START"
./myTask.sh 5 &
./myTask.sh 15 &
./myTask.sh 10 &
wait
echo "DONE"
it doesn't work:
START
Sleeping 5 seconds
Sleeping 15 seconds
Sleeping 10 seconds
DONE
sweber@pc:~/testwait $5 are over!
10 are over!
15 are over!
Of course, I could create multiple targets which can be "built" in parallel by make, or let make just run the bash script which runs the tasks. But is there a way to do it more like what I already tried?
Parallel tasks are split into subtasks that are assigned to multiple workers and then completed simultaneously. A worker system can carry out both parallel and concurrent tasks by working on multiple tasks at the same time while also breaking down each task into sub-tasks that are executed simultaneously.
Parallelism refers to the ability to execute two or more concurrent processes simultaneously. You must have more than one processing core to execute two processes in parallel. Erlang is built for concurrency and will run concurrent solutions (even with a single CPU).
Normally, the make command runs commands sequentially for only one target at a time, waiting for the command to finish before running the next. However, the make command can also run in a parallel run mode, where it can run many concurrent jobs to build independent targets.
To start GNU Make in parallel mode it's enough to specify either the -j or --jobs option on the command-line. The argument to the option is the maximum number of processes that GNU Make will run in parallel. For example, typing make --jobs=4 will allow GNU Make to run up to four subprocesses in parallel.
Each individual logical line in a recipe is invoked in its own shell. So your makefile is basically running this:
test:
/bin/sh -c 'echo "START"'
/bin/sh -c './myTask.sh 5 &'
/bin/sh -c './myTask.sh 15 &'
/bin/sh -c './myTask.sh 10 &'
/bin/sh -c 'wait'
/bin/sh -c 'echo "DONE"'
You should make it all run in a single shell using semicolons and backslash/newlines to combine the physical lines into one logical line:
test:
echo "START"; \
./myTask.sh 5 & \
./myTask.sh 15 & \
./myTask.sh 10 & \
wait; \
echo "DONE"
Why not use the mechanisms already built in make? Something like this:
task%:
sh task.sh $*
test: task5 task15 task10
echo "DONE"
You will be able to adjust the level of parallelism on the make command line, instead of having it hard-coded into your makefile (e.g use 'make -j 2 test' if you have 2 cores available and 'make -j 32 test' if you have 32 cores)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With