Створення єдиного вихідного потоку з трьох інших потоків, вироблених паралельно


10

У мене є три види даних, які є в різних форматах; для кожного типу даних існує сценарій Python, який перетворює його в єдиний єдиний формат.

Цей скрипт Python повільний і пов'язаний з процесором (до одного ядра на багатоядерній машині), тому я хочу запустити три екземпляри цього - по одному для кожного типу даних - і об'єднати їх вихід, щоб передати його sort. В основному, рівнозначно цьому:

{ ./handle_1.py; ./handle_2.py; ./handle_3.py } | sort -n

Але з трьома сценаріями, які працюють паралельно.

Я знайшов це запитання, коли GNU splitвикористовувався для обертання потоку stdout між n екземплярами сценарію, який обробляє потік.

На сторінці розділеної людини:

-n, --number=CHUNKS
          generate CHUNKS output files.  See below
CHUNKS  may be:
 N       split into N files based on size of input
 K/N     output Kth of N to stdout
 l/N     split into N files without splitting lines
 l/K/N   output Kth of N to stdout without splitting lines
 r/N     like 'l'  but  use  round  robin  distributio

Отже r/Nкоманда передбачає " без розбиття рядків ".

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

split -n r/3 -u --filter="./choose_script" << EOF
> 1
> 2
> 3
> EOF

Звідки choose_scriptце:

#!/bin/bash
{ read x; ./handle_$x.py; }

На жаль, я бачу деяке переплетення рядків - і багато нових рядків, яких там не повинно бути.

Наприклад, якщо я заміню свої скрипти Python на кілька простих скриптів bash, які роблять це:

#!/bin/bash
# ./handle_1.sh
while true; echo "1-$RANDOM"; done;

.

#!/bin/bash
# ./handle_2.sh
while true; echo "2-$RANDOM"; done;

.

#!/bin/bash
# ./handle_3.sh
while true; echo "3-$RANDOM"; done;

Я бачу цей вихід:

1-8394

2-11238
2-22757
1-723
2-6669
3-3690
2-892
2-312511-24152
2-9317
3-5981

Це дратує - грунтуючись на витяжці чоловічої сторінки, яку я вставив вище, вона повинна підтримувати цілісність рядка.

Очевидно, що це працює, якщо я вилучу -uаргумент, але тоді він буферизований, і мені не вистачить пам’яті, оскільки він буферизує вихід усіх, крім одного із сценаріїв.

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


Деякі люди в #bash на freenode запропонували мені створити всі три процеси та створити їх фоном, записавши на власні FD, потім перекиньте ці FD і прочитайте рядки для них, але я не зрозумів, як зробити це працездатним. Мені також сказали подивитися на coprocвбудований у bash, хоча я насправді не бачу, як це застосовується.
Цера

1
Чи потрібно це робити без проміжних файлів? Ти не міг просто так зробити job1.py > file1 & job2.py > file 2 & job3.py > file3 ; wait ; sort -n file1 file2 file3?
angus

Відповіді:


2

Спробуйте використовувати параметр -u паралель GNU.

echo "1\n2\n3" | parallel -u -IX ./handle_X.sh

Це запускає їх паралельно, не буферизуючи всю сутність будь-якого процесу.


Я трохи заплутався , - це Xв IXговорити , -Iщо X буде прапор для заміни, або його застосування -Xпрапор, який , здавалося б , також має відповідне значення?
Цера

Хмф. Я роблю це так: parallel -u -X ./handle_{}.sh ::: "1" "2" "3"і, на жаль, я все ще бачу певний вивільнення.
Цера

колишнє: ви також можете використовувати parallel -u ./handle_{}.sh, але я вважаю за краще змінити його, оскільки дужки також мають сенс з'єднати команди (як у вашому запитанні).
блок-блок

Здається, працює на мене, мій греп не сприймає ніякого маніпулювання : pastie.org/5113187 (ти використовуєш скрипти тестових башів чи твої фактичні сценарії Python?)
блокблок

Проблема полягає в тому, що це насправді не робить нічого паралельно. Я використовую сценарії bash - pastie.org/5113225
Cera

2

Спробуйте:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py

Якщо handle_1.pyбереться ім'я файлу:

parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

Ви не хочете, щоб вихід змішувався, тому не використовуйте -u.

Якщо ви хочете зберегти замовлення (таким чином, весь вихід_1_1 є перед обработкой_2, і, таким чином, ви зможете уникнути сортування):

parallel -k  ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*

Якщо ви все ще хочете його сортувати, ви можете паралельно сортувати та використовувати sort -m:

parallel --files "./handle_{1}.py {2} | sort -n"  ::: 1 2 3 ::: files* | parallel -j1 -X sort -m

Встановіть $ TMPDIR на dir, який достатньо великий, щоб утримати вихід.


1
Я хочу, щоб результат був «змішаним» - я просто хочу переконатися, що кожен рядок у кінцевому висновку - це один рядок з одного з підпроцесів. Якщо я його не змішую, у системи не вистачить пам'яті, яка буферизує потоки stdout, які ще не роздруковуються.
Цера

За допомогою GNU Parallel у вас не залишиться пам’яті: вона не буферується в пам’яті. Чому, на вашу думку, він буферизується в пам'яті?
Оле Танге

2

Можливо, мені щось не вистачає, але чи не можеш ти просто зробити:

(./handle_1.py & ./handle_2.py & ./handle_3.py) | sort -n

Якщо ви хочете, щоб рядки кожного процесу не переплутувались, тим простіше, мабуть, переконатися, що сам процес записує їх повністю і, можливо, вимикає буферизацію виводу, оскільки writes до труби гарантовано будуть атомними, якщо вони не перевищують PIPE_BUF. Наприклад, ви можете переконатися , що він робить використання буферизації ля stdioі виклику fflushабо будь-який інший еквівалент в pythonпісля того, як один або декількох рядків були написані.

Якщо ви не можете змінити сценарії python, ви можете зробити:

lb() { grep --line-buffered '^'; }

(з GNU grep) або:

lb() while IFS= read -r l; do printf '%s\n' "$l"; done

(Дивіться примітки в коментарях нижче, якщо те, що команди виводяться, не є текстом)

І роби:

(./handle_1.py | lb & ./handle_2.py | lb & ./handle_3.py | lb) | sort -n

Інший варіант, щоб уникнути цих 3 lbпроцесів, - це три труби до однієї команди, яка використовує select/ pollщоб побачити, звідки надходить якийсь вихід, і подавати його на sortлінійній основі, але це потребує трохи програмування.


waitДумаю, вам потрібен там.
derobert

1
Ні, якщо деякі програми не закриють свій stdout перед виходом, тому що труба і sort -nзалишатиметься до тих пір, поки всі програми, які мають на ній відкритий fd, не вийдуть.
Стефан Шазелас

Дійсно, я перевірив, ти прав.
derobert

Ні, я все одно отримую збиток. Лінії змішуються і переплітаються.
Цера

1
Гаразд @Cerales, дивіться мою оновлену відповідь
Stéphane Chazelas

1

Відповідь Flowbok була правильним рішенням. Як не дивно, висновок GNU parallelозвучується, якщо він виводиться безпосередньо у файл - але не, якщо він переходить до tty.

На щастя, script -cдоступний для імітації TTT.

Є ще три сценарії:

#!/bin/bash
# handle_1.sh
while true; do echo "1-$RANDOM$RANDOM$RANDOM$RANDOM"; done

.

#!/bin/bash
# handle_2.sh
while true; do echo "2-$RANDOM$RANDOM$RANDOM$RANDOM"; done

.

#!/bin/bash
# handle_3.sh
while true; do echo "3-$RANDOM$RANDOM$RANDOM$RANDOM"; done

Потім є файл, який інкапсулює виклик паралельно:

#!/bin/bash
# run_parallel.sh
parallel -u -I N ./handle_N.sh ::: "1" "2" "3"

І тоді я називаю це так:

script -c ./run_parallel > output

Рядки у вихідному сигналі змішуються по черзі між виведеннями різних сценаріїв, але вони не збиваються чи перемежовуються в заданому рядку.

Дивна поведінка від parallel- я можу подати звіт про помилку.

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