Запуск команд паралельно з обмеженням одночасної кількості команд


23

Послідовна: for i in {1..1000}; do do_something $i; done- занадто повільна

Паралельно: for i in {1..1000}; do do_something $i& done- занадто велике навантаження

Як запускати команди паралельно, але не більше, ніж, наприклад, 20 екземплярів на хвилину?

Зараз зазвичай використовують хак, як for i in {1..1000}; do do_something $i& sleep 5; done, але це не гарне рішення.

Оновлення 2 : Перетворена прийнята відповідь у сценарій: http://vi-server.org/vi/parallel

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Зауважте, що ви повинні замінити 8 пробілів двома вкладками перед "i =", щоб він працював.

Відповіді:


15

Для цього створена паралель GNU .

seq 1 1000 | parallel -j20 do_something

Він може навіть виконувати завдання на віддалених комп'ютерах. Ось приклад для повторного кодування MP3 в OGG за допомогою сервера2 та локального комп'ютера, на якому працює 1 завдання на ядро ​​CPU:

parallel --trc {.}.ogg -j+0 -S server2,: \
     'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3

Дивіться вступне відео до GNU Parallel тут:

http://www.youtube.com/watch?v=OpaiGYxkSuQ


Не знав про "moreutils" і що вже є інструмент для роботи. Дивлячись і порівнюючи.
Ві.

1
Модуль parallelin moreutils не є GNU Parallel і є досить обмеженим у своїх можливостях. Команда, наведена вище, не запускається з паралелі з moreutils.
Оле Танге

1
Ще один варіант: xargs --max-procs=20.
Ві.

4

Це не баш-рішення, але ви повинні використовувати Makefile, можливо, -lне перевищуючи деякого максимального навантаження.

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

Тоді, щоб почати 20 робочих моментів одночасно

$ make -j20

або розпочати якомога більше завдань, не перевищуючи навантаження 5

$ make -j -l5

Схоже, поки що не хакізне рішення.
Ві.

2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20Тепер він знову виглядає більш хакітним.
Ві.

@vi: о мій ....
Бенджамін Баньє

Перетворило ваше рішення на сценарій. Тепер ним можна легко користуватися.
Ві.

2

розміщення сценарію у запитанні з форматуванням:

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Зауважте, що ви повинні замінити 8 пробілів двома вкладками перед "i =".


1

Одна проста ідея:

Перевірте, чи я modulo 20, і виконайте команду очікування оболонки перед тим, як зробити до_що.


Він буде або чекати завершення всіх поточних завдань (створення провісів у кількості графіків завдань), або ж чекати одного конкретного завдання, яке може затримуватися довший час (знову створюючи провинки в цьому випадку)
Vi.

@Vi: чекання оболонки - це всі фонові завдання, що належать до цієї оболонки.
harrymc

1

Ви можете використати psпідрахунок кількості запущених процесів, і кожного разу, коли це опускається нижче певного порогу, ви починаєте інший процес.

Псевдокод:

i = 1
MAX_PROCESSES=20
NUM_TASKS=1000
do
  get num_processes using ps
  if num_processes < MAX_PROCESSES
    start process $i
    $i = $i + 1
  endif
  sleep 1 # add this to prevent thrashing with ps
until $i > NUM_TASKS

1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done

Може бути while [ `jobs | wc -l` -ge 20]; do?
Ві.

звичайно, але в моєму зразку я б тоді повинен був обчислити njobsдвічі, а продуктивність досить важлива в сценаріях оболонок, які виконують завдання сну;)
msw

Я маю на увазі, що ваша версія працює не так, як очікувалося. Міняю sleep 1на sleep 0.1і починають середньому njobs до 40-50 замість 20. Якщо є більше 20 робочих місць , нам потрібно чекати , поки будь-яка робота стає закінчена, а не просто чекати 1 секунду.
Ві.

0

ви можете зробити це так.

threads=20
tempfifo=$PMS_HOME/$$.fifo

trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo

for ((i=1; i<=$threads; i++))
do
    echo >&1000
done

for ((j=1; j<=1000; j++))
do
    read -u1000
    {
        echo $j
        echo >&1000
    } &
done

wait
echo "done!!!!!!!!!!"

використовуючи названі труби, кожен раз паралельно він виконує 20 додаткових оболонок.

Сподіваюся, це допоможе :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.