Параллелизируйте сценарий Bash с максимальным количеством процессов

В двух словах легко запомнить PECS

  1. Используйте подстановочный символ <? extends T>, если вам нужно извлечь объект типа T из коллекции.
  2. Используйте кнопку <? super T>, если вам нужно поместить объекты типа T в коллекцию.
  3. Если вам нужно удовлетворить обе вещи, ну, не используйте подстановочный знак. Так просто, как это.
83
задан codeforester 30 April 2017 в 19:54
поделиться

9 ответов

В зависимости от того, что вы хотите сделать, xargs также может помочь (здесь: преобразование документов с помощью pdf2ps):

cpus=$( ls -d /sys/devices/system/cpu/cpu[[:digit:]]* | wc -w )

find . -name \*.pdf | xargs --max-args=1 --max-procs=$cpus  pdf2ps

Из документации:

--max-procs=max-procs
-P max-procs
       Run up to max-procs processes at a time; the default is 1.
       If max-procs is 0, xargs will run as many processes as  possible  at  a
       time.  Use the -n option with -P; otherwise chances are that only one
       exec will be done.
57
ответ дан 24 November 2019 в 08:50
поделиться

Проект я работаю над использованием эти , ожидает команда для управления параллельной оболочкой (ksh на самом деле) процессы. Для обращения к опасениям по поводу IO, на современной ОС, это - возможное параллельное выполнение, на самом деле увеличит эффективность. Если все процессы читают те же блоки на диске, только первый процесс должен будет поразить физическое оборудование. Другие процессы часто будут в состоянии получить блок от дискового кэша ОС в памяти. Очевидно, чтение из памяти является несколькими порядками величины, более быстрыми, чем чтение из диска. Кроме того, преимущество не требует никаких изменений кодирования.

2
ответ дан Jon Ericson 24 November 2019 в 08:50
поделиться

Возможно, попробуйте утилиту параллелизации, вместо этого переписав цикл? Я - большой поклонник xjobs. Я использую xjobs все время для массовых файлов копии через нашу сеть, обычно при установке нового сервера базы данных. http://www.maier-komor.de/xjobs.html

8
ответ дан tessein 24 November 2019 в 08:50
поделиться

Вместо простого удара используйте Make-файл, затем определите количество одновременных заданий с make -jX, где X количество заданий для выполнения сразу.

Или можно использовать wait (" man wait"): запустите несколько дочерних процессов, звоните wait - это выйдет, когда дочерние процессы закончатся.

maxjobs = 10

foreach line in `cat file.txt` {
 jobsrunning = 0
 while jobsrunning < maxjobs {
  do job &
  jobsrunning += 1
 }
wait
}

job ( ){
...
}

, Если необходимо сохранить результат задания, затем присвойте их результат переменной. После wait Вы просто проверяете то, что содержит переменная.

11
ответ дан skolima 24 November 2019 в 08:50
поделиться
maxjobs=4
parallelize () {
        while [ $# -gt 0 ] ; do
                jobcnt=(`jobs -p`)
                if [ ${#jobcnt[@]} -lt $maxjobs ] ; then
                        do-something $1 &
                        shift  
                else
                        sleep 1
                fi
        done
        wait
}

parallelize arg1 arg2 "5 args to third job" arg4 ...
23
ответ дан euphoria83 24 November 2019 в 08:50
поделиться

Хотя сделать это правильно в bash , вероятно, невозможно, вы можете довольно легко сделать полуправо. bstark дал хорошее приближение к праву, но у него есть следующие недостатки:

  • Разделение слов: вы не можете передать ему задания, которые используют в своих аргументах любой из следующих символов: пробелы, табуляции, новые строки, звездочки, вопросительные знаки. Если вы это сделаете, что-то сломается, возможно, неожиданно.
  • Он полагается на остальную часть вашего скрипта, чтобы ничего не делать в фоновом режиме. Если вы это сделаете или позже вы добавите что-то в сценарий, который будет отправлен в фоновом режиме, потому что вы забыли, что вам не разрешено использовать фоновые задания из-за его фрагмента, все сломается.

Другое приближение, которое не имеет этих Недостатки следующие:

scheduleAll() {
    local job i=0 max=4 pids=()

    for job; do
        (( ++i % max == 0 )) && {
            wait "${pids[@]}"
            pids=()
        }

        bash -c "$job" & pids+=("$!")
    done

    wait "${pids[@]}"
}

Обратите внимание, что этот легко адаптируется, чтобы также проверять код выхода каждого задания при его завершении, чтобы вы могли предупреждать пользователя, если задание не удается, или установить код выхода для scheduleAll в соответствии с количеством задание, которое не удалось, или что-то в этом роде.

Проблема с этим кодом как раз в том, что:

  • Он планирует четыре (в данном случае) задания за раз и затем ожидает завершения всех четырех. Некоторые из них могут быть выполнены раньше, чем другие, что приведет к тому, что следующий пакет из четырех заданий будет ждать завершения самого длинного из предыдущего пакета.

Решение, которое решает эту последнюю проблему, должно использовать kill -0 для опроса, не исчез ли какой-либо из процессов, вместо ожидания и планирования следующего задания. Однако это создает небольшую новую проблему: у вас есть состояние гонки между окончанием задания, и kill -0 проверяет, закончился ли он. Если задание завершилось, и в то же время запускается другой процесс в вашей системе, принимая случайный PID, который является идентификатором только что завершенного задания, kill -0 не заметит, что ваша работа имеет закончено, и все снова сломается.

Идеальное решение невозможно в bash .

6
ответ дан 24 November 2019 в 08:50
поделиться

Вот альтернативное решение, которое можно вставить в .bashrc и использовать для повседневного использования одного лайнера:

function pwait() {
    while [ $(jobs -p | wc -l) -ge $1 ]; do
        sleep 1
    done
}

Чтобы использовать его, все, что нужно сделать, это поставить & после задания и вызов pwait, параметр дает количество параллельных процессов:

for i in *; do
    do_something $i &
    pwait 10
done

Было бы лучше использовать wait вместо ожидания занятости на выходе jobs -p , но, похоже, нет очевидного решения - дождаться завершения любого из заданных заданий вместо их всех.

11
ответ дан 24 November 2019 в 08:50
поделиться

If you're familiar with the make command, most of the time you can express the list of commands you want to run as a a makefile. For example, if you need to run $SOME_COMMAND on files *.input each of which produces *.output, you can use the makefile

INPUT  = a.input b.input
OUTPUT = $(INPUT:.input=.output)

%.output : %.input
    $(SOME_COMMAND) $< $@

all: $(OUTPUT)

and then just run

make -j<NUMBER>

to run at most NUMBER commands in parallel.

6
ответ дан 24 November 2019 в 08:50
поделиться

С помощью GNU Parallel http://www.gnu.org/software/parallel/ вы можете написать:

some-command | parallel do-something

GNU Parallel также поддерживает выполнение заданий на удаленных компьютерах. При этом задания будут выполняться по одному на каждое ядро процессора на удаленных компьютерах - даже если у них разное количество ядер:

some-command | parallel -S server1,server2 do-something

Более продвинутый пример: Здесь мы перечисляем файлы, которые мы хотим, чтобы my_script выполнял. Файлы имеют расширение (возможно, .jpeg). Мы хотим, чтобы вывод my_script был помещен рядом с файлами в basename.out (например, foo.jpeg -> foo.out). Мы хотим запустить my_script один раз для каждого ядра компьютера, и мы хотим запустить его на локальном компьютере тоже. Для удаленных компьютеров мы хотим, чтобы обрабатываемый файл передавался на данный компьютер. Когда my_script завершит работу, мы хотим, чтобы foo.out был передан обратно, а затем мы хотим, чтобы foo.jpeg и foo.out были удалены с удаленного компьютера:

cat list_of_files | \
parallel --trc {.}.out -S server1,server2,: \
"my_script {} > {.}.out"

GNU Parallel следит за тем, чтобы вывод каждого задания не смешивался, поэтому вы можете использовать вывод как вход для другой программы:

some-command | parallel do-something | postprocess

Дополнительные примеры смотрите на видео: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

38
ответ дан 24 November 2019 в 08:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: