Паралельні петлі оболонки


11

Я хочу обробити багато файлів, і оскільки у мене тут є маса ядер, я хочу це робити паралельно:

for i in *.myfiles; do do_something $i `derived_params $i` other_params; done

Я знаю рішення Makefile, але мої команди потребують аргументів із списку глобальних оболонок. Я знайшов:

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

Щоб скористатися ним, все, що потрібно зробити - це поставити & після виконання завдань і дзвінка з підписом, параметр дає кількість паралельних процесів:

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

Але це працює не дуже добре, наприклад, я спробував це, наприклад, для циклу, який перетворює багато файлів, але дає мені помилку і не залишає завдання скасованим.

Я не можу повірити, що це ще не зроблено, оскільки дискусія в списку розсилки zsh на сьогодні вже така стара. Так ти знаєш краще?


Подібно до цього питання: superuser.com/questions/153630/… Подивіться, чи працює ця методика для вас.
JRobert

Було б корисно, якщо ви опублікували повідомлення про помилки.
Призупинено до подальшого повідомлення.

@JRobert Так, я це знав, але це насправді не допомагає, оскільки підхід до makefile не працюватиме, як я вже сказав! @Dennis: Гаразд, спочатку я дозволю запустити верхній бік, показуючи мені більше, ніж вказана кількість процесів. По-друге, він не повертається до запиту належним чином. По-третє, що я сказав, що це залишає завдання невідомими: я просто розмістив індикатор echo "DONE"після циклу, який був виконаний до того, як активні завдання не будуть закінчені. => Це змусило мене думати, що роботи не були виконані.
математика

Відповіді:


15

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

Вимога до глобалізації не є перешкодою: є такі реалізації, які підтримують її. GNU make, який має розширені підстановки, такі як $(wildcard *.c)і доступ до оболонки, наприклад $(shell mycommand)(шукайте функції у посібнику GNU для отримання додаткової інформації). Це за замовчуванням makeв Linux і доступний у більшості інших систем. Ось скелет Makefile, який ви, можливо, зможете адаптувати під свої потреби:

джерела = $ (wildcard * .src)

all: $ (джерела: .src = .tgt)

% .tgt: $ .src
    do_something $ <$$ (deriva_params $ <)> $ @

Виконайте щось на кшталт make -j4виконання паралельно чотирьох завдань або make -j -l3збереження середнього навантаження близько 3.


8

Я не впевнений, що таке ваші похідні аргументи. Але з GNU Parallel http: // www.gnu.org/software/parallel/ ви можете зробити це, щоб виконати одну роботу на ядро ​​процесора:

find . | parallel -j+0 'a={}; name=${a##*/}; upper=$(echo "$name" | tr "[:lower:]" "[:upper:]");
   echo "$name - $upper"'

Якщо те, що ви хочете отримати, - це просто змінити розширення. {{}}:

parallel -j+0 lame {} -o {.}.mp3 ::: *.wav

Перегляньте вступне відео до GNU Parallel на веб- сайті http://www.youtube.com/watch?v=OpaiGYxkSuQ


7

Чи не використовує команда оболонки waitдля вас роботу?

for i in *
do
    do_something $i &
done
wait

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


ні, з 1 мільйоном файлів у мене буде 1 мільйон процесів, або я помиляюся?
математика

1
@brubelsabs: Ну, спробували б зробити мільйон процесів. Ви не сказали у своєму питанні, скільки файлів потрібно обробити. Я думаю, вам потрібно використовувати вкладені forпетлі, щоб обмежити це: for file in *; do for i in {1..10}; do do_something "$i" & done; wait; done(неперевірене). Це потрібно робити десять за один раз і чекати, поки всі десять кожної групи будуть виконані, перш ніж починати наступну десятку. Ваша петля робить по черзі &суперечки. Дивіться питання, з яким JRobert пов'язував інші варіанти. Шукайте в переповнюванні стека інші питання, подібні до вашого (і того).
Призупинено до подальшого повідомлення.

Якщо ОП передбачає мільйон файлів, тоді у нього виникнуть проблеми for i in *. Йому доведеться передавати аргументи до петлі з трубою чи щось. Тоді замість внутрішньої петлі ви можете запустити збільшення лічильника і запустити "micro-"wait"-s"кожен "$ ((i% 32))" -eq '0'

@DennisWilliamson: комбінування waitз внутрішньою петлею лічильника добре працювало для мене. Спасибі!
Джоел Пурра

3

Чому ще ніхто не згадав xargs?

Якщо припустити, що у вас рівно три аргументи,

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; done | xargs -n 3 -P $PROCS do_something

В іншому випадку використовуйте роздільник (нуль для цього зручно):

for i in *.myfiles; do echo -n $i `derived_params $i` other_params; echo -ne "\0"; done | xargs -0 -n 1 -P $PROCS do_something

EDIT: для вищезазначеного кожен параметр повинен бути розділений нульовим символом, а потім кількість параметрів слід вказати за допомогою xargs -n.


Так, у нашому проекті хтось мав таку ж ідею, і він чудово працює навіть під Windows з MSys.
математика

0

Я спробував деякі відповіді. Вони роблять сценарій трохи складнішим, ніж це потрібно. В ідеалі використання parallelабо xargsбуло б бажано, але якщо операції всередині циклу for є складними, це може бути проблематично для створення великих і довгих файлів рядків для подачі паралельно. замість цього ми могли б використовувати джерело наступним чином

# Create a test file 
$ cat test.txt
task_test 1
task_test 2

# Create a shell source file 
$ cat task.sh
task_test()
{
    echo $1
}

# use the source under bash -c 
$ cat test.txt | xargs -n1 -I{} bash -c 'source task.sh; {}'
1
2

Таким чином, для Вашого вирішення проблеми буде виглядати

for i in *.myfiles; echo " do_something $i `derived_params $i` other_params
" >> commands.txt ; done

визначити робити щось як do_something.sh

do_something(){
process $1
echo $2 
whatever $3 

}

виконати за допомогою xargабоgnu parallel

   cat commands.txt | xargs -n1 -I{} -P8 bash -c 'source do_something.sh; {}'

Я припускаю, що функціональна незалежність ітерацій for має на увазі.

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