використовуючи паралельну обробку унікальних вхідних файлів до унікальних вихідних файлів


18

У мене є проблема зі скриптом оболонки, де мені надається каталог, що містить повний вхідний файл (кожен файл містить багато рядків введення), і мені потрібно обробляти їх окремо, перенаправляючи кожен їх вихід на унікальний файл (він же, файл_1.input потребує бути захопленим у file_1.output тощо).

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

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

Будь-яка допомога буде вдячна!

Приклад вхідного каталогу:

> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt

Сценарій:

> cat proces_script.sh
#!/bin/sh

customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]

Оновлення : Прочитавши відповідь Оле, представлену нижче, я зміг зібрати пропущені фрагменти для моєї власної паралельної реалізації. Хоча його відповідь чудова, ось моє додаткове дослідження та замітки, які я взяв:

Замість того, щоб запустити мій повний процес, я зрозумів, що почати з доказів концепції концепції, щоб довести його рішення в моєму середовищі. Перегляньте дві мої різні реалізації (та примітки):

find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out

Використовує find (не ls, що може спричинити проблеми), щоб знайти всі застосовні файли в моєму каталозі вхідних файлів, а потім перенаправляє їх вміст в окремий каталог та файл. Моє питання зверху було читанням і перенаправленням (власне сценарій був простим), тому заміна сценарію котом була прекрасним доказом концепції.

parallel cat '>' /home/me/output_files/{.}.out :::  /home/me/input_files/*

Це друге рішення використовує парадигму змінної паралельної вхідної системи для читання файлів, однак для початківців це було набагато заплутаніше. Для мене, використовуючи функцію find a and pipe, просто задовольнили мої потреби.

Відповіді:


27

GNU Parallel призначений для таких завдань:

parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output ::: *.input

або:

ls | parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output

Він буде виконувати одне завдання на ядро ​​CPU.

Ви можете встановити GNU Parallel просто:

wget https://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

Перегляньте вступні відео для паралельних GNU, щоб дізнатися більше: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


Відмінна відповідь (і основні моменти щодо читання мого запиту про використання паралельно).
Дж. Джонс

5

Стандартний спосіб зробити це - встановити чергу та породити будь-яку кількість працівників, які знають, як витягнути щось із черги та обробити її. Для зв'язку між цими процесами можна використовувати фіфо (він називається труба).

Нижче наведено наївний приклад продемонструвати концепцію.

Простий сценарій черги:

#!/bin/sh
mkfifo /tmp/location-queue
for i in inputfiles/*; do
  echo $i > /tmp/location-queue
done
rm /tmp/location-queue

І працівник:

#!/bin/sh
while read file < /tmp/location-queue; do
  process_file "$file"
done

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

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

Сценарій монітора:

#!/bin/sh
queue.sh &
num_workers="$1"
i=0
while [ $i < $num_workers ]; do
  worker.sh &
  echo $! >> /tmp/worker.pids
  i=$((i+1))
done
monitor_workers

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


Яким чином монітор достатньо розумний, щоб призупинити нерест нових робітників, поки не закінчиться наступний (він же, де $ i коли-небудь зменшується)? ---- Відповідаючи на мою власну редакцію, працівники ніколи не йдуть, вони просто обробляють файли, поки вся обробка не буде вичерпана (звідси і цикл часу в межах "процесорів").
Дж. Джонс

Який рядок "monitor_workers" в кінці виконання сценарію монітора?
Дж. Джонс

@JJones - monitor_workersпросто так process_file- це функція, яка робить все, що завгодно. Щодо монітора - ви мали рацію; він повинен зберігати підс своїх працівників (щоб він міг надсилати сигнал вбивства), а лічильник потрібно нарощувати, коли він заводить працівника. Я відредагував відповідь, щоб включити її.
Шон Дж. Гофф

Я дуже ціную вашу роботу, але, думаю, вам слід скористатися GNU parallel. Я думаю, що це ваша ідея, повністю реалізована.
motobói

5

Інший приклад:

ls *.txt | parallel 'sort {} > {.}.sorted.txt'

Інші приклади я виявив надмірно складними, коли в більшості випадків саме ви, можливо, шукали вище.


4

Зробити загальнодоступний інструмент, який може проводити паралелізацію. GNU make та деякі інші мають -jможливість виконувати паралельні побудови.

.SUFFIXES: .input .output
.input.output:
        process_one_file <$< >$@.tmp
        mv -f $@.tmp $@

Виконайте makeтак (я вважаю, що ваші файли не містять жодних спеціальних символів, makeце не добре)

make -j 4 $(for x in *.input; do echo ${x%.*}.output; done)

imho це найрозумніше рішення :)
h4unt3r

3

Це потрібно для виконання тієї самої команди для великого набору файлів у поточному каталозі:

#!/bin/sh
trap 'worker=`expr $worker - 1`' USR1  # free up a worker
worker=0  # current worker
num_workers=10  # maximum number of workers
for file in *.txt; do
    if [ $worker -lt $num_workers ]; then
        {   customScript -c 33 -I -file $file -a -v 55 > `basename $file .txt`.outtxt 
            kill -USR1 $$ 2>/dev/null  # signal parent that we're free
        } &
        echo $worker/$num_worker $! $file  # feedback to caller
        worker=`expr $worker + 1`
    else
        wait # for a worker to finish
    fi
done

Це запускається у customScriptкожному txtфайлі, ставлячи вихід у outtxtфайли. Змінюйтеся, як вам потрібно. Ключовим фактором для роботи цього є обробка сигналів за допомогою SIGUSR1, щоб дочірній процес міг повідомити батьківському процесу, що це робиться. Використання SIGCHLD не буде працювати, оскільки більшість висловлювань у скрипті генерують сигнали SIGCHLD до сценарію оболонки. Я спробував це, замінивши вашу команду sleep 1, програма використовувала 0,28s cpu користувача та 0,14s системного процесора; це було лише на близько 400 файлів.


Яким чином "чекати" достатньо розумним, щоб взяти той самий файл, який зараз переглядається, та повторно ввести виступ "якщо"?
Дж. Джонс

Це не те, waitщо достатньо «розумне»; але він повернеться після отримання SIGUSR1сигналу. Дитина / працівник надсилає SIGUSR1батько, який потрапив ( trap), і скорочення $worker( trapпункт) і повертається ненормально wait, дозволяючи if [ $worker -lt $num_workers ]виконати пропозицію.
Арседж

0

Або просто використовувати xargs -P, не потрібно встановлювати додаткове програмне забезпечення:

find . -type f -print0 | xargs -0 -I'XXX' -P4 -n1 custom_script -input "XXX" -output "XXX.out"

Трохи пояснення для варіантів:

  • -I'XXX' встановлює рядок, який буде замінено в шаблоні команди на ім'я файлу
  • -P4 буде виконувати 4 процеси паралельно
  • -n1 буде розміщено лише один файл на виконання, навіть якщо знайдено два XXX
  • -print0і -0працюйте разом, дозволяючи мати особливі символи (наприклад, пробіли) у назвах файлів
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.