Минимальная “Очередь задач” со стандартными инструментами Linux для использования многоядерного процессора
-
06-07-2019 - |
Вопрос
Каков наилучший / самый простой способ создать минимальную систему очередей задач для Linux с использованием bash и обычных инструментов?
У меня есть файл с 9 000 строками, каждая строка содержит командную строку bash, команды полностью независимы.
command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
...
В моем ящике более одного ядра, и я хочу выполнять X задач одновременно.Я поискал в Интернете хороший способ сделать это.Очевидно, что у многих людей есть эта проблема, но пока ни у кого нет хорошего решения.
Было бы неплохо, если бы решение обладало следующими функциями:
- может интерпретировать более одной команды (например
command; command
) - может интерпретировать перенаправления потока в строках (например
ls > /tmp/ls.txt
) - использует только обычные инструменты Linux
Бонусные баллы, если он работает на других Unix-клонах без слишком экзотических требований.
Решение
Можете ли вы преобразовать свой список команд в Makefile?Если это так, вы могли бы просто запустить "make -j X".
Другие советы
GNU Параллельный http://www.gnu.org/software/parallel/ является более общим инструментом для распараллеливания, чем PPSS.
Если файл runfile содержит:
command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
вы можете сделать:
cat runfile | parallel -j+0
который будет запускать по одной команде на ядро процессора.
Если ваши команды так же просты, как описано выше, вам даже не нужен runfile, но вы можете сделать:
seq 1 3 | parallel -j+0 'command {} > Logs/{}.log'
Если у вас есть больше компьютеров, доступных для выполнения обработки, вы можете захотеть посмотреть на опции --sshlogin и --trc для GNU Parallel.
Хорошо, разместив вопрос здесь, я нашел следующий проект, который выглядит многообещающим: ппсс.
Редактировать:Не совсем то, что я хочу, PPSS ориентирован на обработку "всех файлов в каталоге A".
Ну, в любом случае, это своего рода забавный вопрос.
Вот что бы я сделал, предполагая удар (1) конечно.
- выясните, сколько из этих команд могут с пользой выполняться одновременно.Дело будет не только в количестве ядер;многие команды будут приостановлены для ввода-вывода и тому подобного.Позвоните по этому номеру N.
N=15
например. - настройте обработчик сигнала перехвата для сигнала SIGCHLD, который возникает при завершении дочернего процесса.
trap signalHandler SIGCHLD
- поместите свой список команд в канал
- напишите цикл, который считывает stdin и выполняет команды одну за другой, уменьшая счетчик.Когда счетчик равен 0, он
wait
s. - ваш обработчик сигналов, который выполняется на SIGCHLD, приращения этот счетчик.
Итак, теперь он запускает первый N
отдает команды, затем ждет.Когда первый дочерний процесс завершается, ожидание возвращается, оно считывает другую строку, запускает новую команду и снова ожидает.
Так вот, это случай, когда многие задания заканчиваются близко друг к другу.Я подозреваемый вам может сойти с рук более простая версия:
N=15
COUNT=N
cat mycommands.sh |
while read cmd
do
eval $cmd &
if $((count-- == 0))
then
wait
fi
od
Теперь эта программа запустит первые 15 команд, а затем выполнит остальные по одной, когда какая-то команда завершится.
Аналогичной забавой для распределенных вычислений является скрипт Mapreduce Bash:
http://blog.last.fm/2009/04/06/mapreduce-bash-script
И спасибо, что указали на ppss!
Вы можете использовать ксарги команда, ее --макс-процы делает то, что ты хочешь.Например, решение Чарли Мартина становится с помощью xargs:
tr '\012' '\000' <mycommands.sh |xargs --null --max-procs=$X bash -c
Подробные сведения:
- X - максимальное количество процессов.Например,:Х=15.--max-procs творит чудеса
- первый tr здесь предназначен для завершения строк нулевыми байтами для параметра xargs --null, чтобы перенаправление кавычек и т.д. Не Были неправильно расширены
- bash -c запускает команду
Я протестировал это с помощью этого mycommands.sh например, файла:
date
date "+%Y-%m-%d" >"The Date".txt
wc -c <'The Date'.txt >'The Count'.txt
Это особый случай, но если вы пытаетесь обработать набор файлов и создать другой набор выходных файлов, вы можете запустить #core number процессов и проверить, существует ли выходной файл, прежде чем обрабатывать его.Приведенный ниже пример преобразует каталог с файлами формата .m4b в файлы формата .mp3:
Просто запустите эту команду столько раз, сколько у вас есть ядер:
ls *m4b|во время чтения f;выполнить тест -f ${f%m4b}mp3 || mencoder -of rawaudio "$f" -oac mp3lame -ovc копировать -o $ {f%m4b}mp3;Выполнено &
Вы могли бы увидеть мою очередь задач, написанную на bash: https://github.com/pavelpat/yastq
Очередь задач + Распараллеливание + Динамическое добавление
Используя FIFO, этот скрипт разветвляется для обработки очереди.Таким образом, вы можете добавлять команды в очередь "на лету" (когда очередь уже запущена).
Использование:./команда очереди [количество дочерних элементов] [Имя очереди]
Пример, с 1 потоком:
./queue "sleep 5; echo ONE" ./queue "echo TWO"
Выходной сигнал:
ONE TWO
Пример, с 2 потоками:
./queue "sleep 5; echo ONE" 2 ./queue "echo TWO"
Выходной сигнал:
TWO ONE
Пример, с 2 очередями:
./queue "sleep 5; echo ONE queue1" 1 queue1 ./queue "sleep 3; echo ONE queue2" 1 queue2
Выходной сигнал:
ONE queue2 ONE queue1
Скрипт (сохраните его как "queue" и chmod +x queue):
#!/bin/bash #Print usage [[ $# -eq 0 ]] && echo Usage: $0 Command [# of children] [Queue name] && exit #Param 1 - Command to execute COMMAND="$1" #Param 2 - Number of childs in parallel MAXCHILD=1 [[ $# -gt 1 ]] && MAXCHILD="$2" #Param 3 - File to be used as FIFO FIFO="/tmp/defaultqueue" [[ $# -gt 2 ]] && FIFO="$3" #Number of seconds to keep the runner active when unused TIMEOUT=5 runner(){ #Associate file descriptor 3 to the FIFO exec 3"$FIFO" while read -u 3 -t $TIMEOUT line; do #max child check while [ `jobs | grep Running | wc -l` -ge "$MAXCHILD" ]; do sleep 1 done #exec in backgroud (eval "$line")& done rm $FIFO } writer(){ #fork if the runner is not running lsof $FIFO >/dev/null || ($0 "QueueRunner" "$MAXCHILD" "$FIFO" &) #send the command to the runner echo "$COMMAND" > $FIFO } #Create the FIFO file [[ -e "$FIFO" ]] || mkfifo "$FIFO" #Start the runner if in the runner fork, else put the command in the queue [[ "$COMMAND" == "QueueRunner" ]] && runner || writer