Использование make для параллельного выполнения независимых задач
У меня есть несколько команд, которые я хотел бы выполнять параллельно. Команды почти идентичны. Можно ожидать, что они займут примерно одинаковое время и могут работать совершенно независимо. Они могут выглядеть так:
command -n 1 > log.1
command -n 2 > log.2
command -n 3 > log.3
...
command -n 4096 > log.4096
Я мог бы запустить их все параллельно в сценарии оболочки, но система попыталась бы загрузить больше, чем строго необходимо, чтобы поддерживать занятость ЦП (каждая задача занимает 100% одного ядра до завершения). Это заставит диск треснуть и сделает все это медленнее, чем менее жадный подход к выполнению.
Лучше всего придерживаться n
выполнение задач, где n
количество доступных ядер.
Я стремлюсь не изобретать велосипед. Эта проблема уже решена в Unix make
программа (при использовании с -j n
опция). Мне было интересно, возможно ли было написать общие правила Makefile для вышеупомянутого, чтобы избежать Makefile линейного размера, который выглядел бы так:
all: log.1 log.2 ...
log.1:
command -n 1 > log.1
log.2:
command -n 2 > log.2
...
Если лучшее решение не использовать make
но другая программа / утилита, я открыт для этого, пока зависимости разумны (make
было очень хорошо в этом отношении).
5 ответов
Смотрите шаблон правил
Другой способ, если это единственная причина, почему вам нужно make
это использовать -n
а также -P
варианты xargs
,
Вот более переносимый код оболочки, который не зависит от расширения скобки:
LOGS:= $ (shell seq 1 1024)
Обратите внимание на использование:=, чтобы определить более эффективную переменную: просто расширенный "аромат".
xargs -P - это "стандартный" способ сделать это. Обратите внимание, что в зависимости от дискового ввода-вывода вы можете ограничить количество шпинделей, а не ядер. Если вы хотите ограничиться ядрами, обратите внимание на новую команду nproc в недавних coreutils.
Сначала легкая часть. Как отмечает Роман Чепляка, правила шаблонов очень полезны:
LOGS = log.1 log.2 ... log.4096
all: $(LOGS)
log.%:
command -n $* > log.$*
Сложная часть создает этот список, LOGS
, Марка не очень хороша в обработке чисел. Наилучший способ - это, вероятно, вызвать оболочку. (Возможно, вам придется настроить этот сценарий для вашей оболочки - сценарии оболочки не моя самая сильная тема.)
NUM_LOGS = 4096
LOGS = $(shell for ((i=1 ; i<=$(NUM_LOGS) ; ++i)) ; do echo log.$$i ; done)
С GNU Parallel вы написали бы:
parallel command -n {} ">" log.{} ::: {1..4096}
10 секундная установка:
(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash
Подробнее: http://www.gnu.org/software/parallel/parallel_tutorial.html https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1