Использование 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

Другие вопросы по тегам