Виконання трубних команд паралельно


16

Розглянемо наступний сценарій. У мене є дві програми A і B. Програма A виводить рядки рядків stdout, тоді як програма B технологічні лінії від stdin. Звичайно, спосіб використання цих двох програм:

foo @ bar: ~ $ A | Б

Тепер я помітив, що це з'їдає лише одне ядро; отже мені цікаво:

Чи обмінюються програми A і B однаковими обчислювальними ресурсами? Якщо так, чи існує спосіб одночасного запуску A і B?

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

Тобто, A виводить свої рядки, і буде N екземплярів програм B, які читатимуть ці рядки (хто б їх читав першими) обробляли їх та виводили на stdout.

Отже, моє остаточне питання:

Чи є спосіб передати вихід на A серед декількох B-процесів, не піклуючись про умови перегонів та інші невідповідності, які потенційно можуть виникнути?


1
Хоча A | B | Cпаралельно, як і в окремих процесах, через характер труб (B доводиться чекати виходу A, C повинен чекати виходу B), в деяких випадках він все ще може бути лінійним. Це повністю залежить від того, який вид продукції вони виробляють. Не так багато випадків, коли запуск декількох Bдопоможе багато, цілком можливо, що приклад паралельного wc повільніше, ніж регулярний, wcоскільки розщеплення може зайняти більше ресурсів, ніж підрахунок рядків. Використовуйте обережно.
frostschutz

Відповіді:


14

Проблема split --filterполягає в тому, що результат може бути змішаний, тому ви отримуєте половину рядка з процесу 1, а потім половину рядка з процесу 2.

GNU Паралельно гарантує, що не буде змішуватися.

Тож припустимо, що ви хочете зробити:

 A | B | C

Але ця B страшенно повільна, і, таким чином, ви хочете паралелізувати це. Тоді ви можете зробити:

A | parallel --pipe B | C

GNU Паралельно за замовчуванням розбивається на \ n і розмір блоку становить 1 Мб. Це можна відрегулювати за допомогою --recend та --block.

Більше про GNU Parallel можна знайти за посиланням: http://www.gnu.org/s/parallel/

Ви можете встановити GNU Parallel всього за 10 секунд за допомогою:

wget -O - pi.dk/3 | sh 

Перегляньте вступне відео на http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


1
Поки я категорично не згоден з методом установки :-), +1, оскільки ваше рішення вирішує більшість проблем з моїм.
ЛСерні

Це справді приємно. Чи є у вас також пропозиції щодо параметрів, які слід використовувати? Я знаю, що програма A видасть більше 1 ТБ даних приблизно 5 Гб в хвилину. Програма B обробляє дані в 5 разів повільніше, ніж A, виводить їх, і я маю 5 ядер у своєму розпорядженні для цього завдання.
Джерней

Наразі GNU Parallel може максимум обробляти близько 100 МБ / с, тому ви збираєтесь торкнутися цієї межі. Оптимальний --block-sizeбуде залежати від обсягу оперативної пам’яті та наскільки швидко ви можете запустити нову B. У вашій ситуації я би використовував --block 100Mі бачив, як це робилося.
Оле Танге

@lserni Чи можете ви придумати кращий спосіб установки, який працює на більшості машин UNIX і вимагає від користувача аналогічного обсягу роботи?
Оле Танге

4
Вибачте, я не дав зрозуміти. Метод установки - сценарій, переданий до sh- чудовий. Проблема полягає в тому, щоб передати його на sh: завантаження та запуск виконуваного коду з сайту . Зверніть увагу, можливо, я просто надто параноїчний, оскільки можна заперечити, що на замовлення RPM або DEB - це в основному те саме, і навіть розміщення коду на сторінці, яка буде скопійована і вставлена, призведе до того, що люди роблять це сліпо все одно.
LSerni

13

Коли ви пишете A | B, обидва процеси вже працюють паралельно. Якщо ви бачите, що вони використовують лише одне ядро, це, мабуть, через налаштування спорідненості процесора (можливо, є якийсь інструмент для нерестування процесу з різною спорідненістю) або тому, що одного процесу недостатньо, щоб вмістити все ядро ​​та систему " вважає за краще "не поширювати обчислення.

Для запуску декількох B з одним A, вам потрібен такий інструмент, як splitз --filterопцією:

A | split [OPTIONS] --filter="B"

Це, однак, може зіпсувати порядок рядків на виході, оскільки завдання B не працюватимуть з однаковою швидкістю. Якщо це проблема, можливо, вам доведеться перенаправити B i-й вихід на проміжний файл і з’єднати їх разом в кінці, використовуючи cat. Це, у свою чергу, може зажадати значного місця на диску.

Існують і інші варіанти (наприклад, ви можете обмежити кожен екземпляр B лише одним вихідним рядком, зачекайте, поки закінчиться цілий "раунд" B, запустіть еквівалент зменшення до splitS » карт , а також catтимчасовий вихід разом), з різним рівнем ефективності. Описаний, наприклад, "круглий" варіант буде чекати, коли закінчиться найповільніший екземпляр B , тому він буде сильно залежати від наявного буферизації для B; [m]bufferможе допомогти, а може і не, залежно від операцій.

Приклади

Створіть перші 1000 чисел і підрахуйте рядки паралельно:

seq 1 1000 | split -n r/10 -u --filter="wc -l"
100
100
100
100
100
100
100
100
100
100

Якщо ми мали б "позначити" рядки, ми побачили б, що кожен перший рядок надсилається обробці №1, кожен п'ятий рядок - обробці №5 тощо. Більше того, за час, який потрібен splitдля нерестування другого процесу, перший вже є хорошим способом його квоти:

seq 1 1000 | split -n r/10 -u --filter="sed -e 's/^/$RANDOM - /g'" | head -n 10
19190 - 1
19190 - 11
19190 - 21
19190 - 31
19190 - 41
19190 - 51
19190 - 61
19190 - 71
19190 - 81

Під час виконання на двоядерній машині seq, splitіwc процеси поділяють ядра; але придивившись ближче, система залишає перші два процеси на CPU0 і розділяє CPU1 між робочими процесами:

%Cpu0  : 47.2 us, 13.7 sy,  0.0 ni, 38.1 id,  1.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  : 15.8 us, 82.9 sy,  0.0 ni,  1.0 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM     TIME+ COMMAND
 5314 lserni    20   0  4516  568  476 R 23.9  0.0   0:03.30 seq
 5315 lserni    20   0  4580  720  608 R 52.5  0.0   0:07.32 split
 5317 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.86 wc
 5318 lserni    20   0  4520  572  484 S 14.0  0.0   0:01.88 wc
 5319 lserni    20   0  4520  576  484 S 13.6  0.0   0:01.88 wc
 5320 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.85 wc
 5321 lserni    20   0  4520  572  484 S 13.3  0.0   0:01.84 wc
 5322 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.86 wc
 5323 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.86 wc
 5324 lserni    20   0  4520  576  484 S 13.3  0.0   0:01.87 wc

Зверніть увагу, особливо, що ви splitїсте значна кількість процесора. Це зменшиться пропорційно потребам А; тобто, якщо A є більш важким процесом, ніж seqвідносний наклад витрат splitзменшиться. Але якщо А це дуже легкий процес і B досить швидко (так , що вам потрібно не більше 2-3 Б, щоб тримати разом з А), то розпаралелювання з split(або труби в цілому) може бути і не варто.


Цікаво, що розкол, знайдений на Ubuntu, не має опції --filter. Які ОС використовуються для цього?
Jernej

Linux OpenSuSE 12.3, з coreutils ( gnu.org/software/coreutils/manual/html_node/… ). Я спробую роздобути Ubuntu, вони, можливо, змінили назву, щоб вмістити якийсь інструмент з аналогічним назвою.
LSerni

Ви впевнені, що split --filterваріант відсутній? У моєму Ubuntu 12.04-LTS ("wheezy / sid") він є, і мої приклади працюють. Чи можете ви встановити інший, splitніж той, що знаходиться в GNU coreutils?
LSerni

Дякую за це Довелося встановити новішу версію Coreutils. До речі, я помітив, що якщо я запускаю програму A, вона з'їдає все ядро ​​(100%), якщо я запускаю A | Тоді вони разом їдять ціле ядро, обробляють A 15%, а B їдять 85%. Чи трапляється ви, чому це так?
Jernej

2
Це, ймовірно, через блокування . Якщо B важчий за A, то A не може надіслати вихід і сповільнюється. Іншою можливістю є вихід на B під час його роботи (наприклад, диск / мережа). У іншій системі ви можете бачити, що B похизує 100% CPU1, а A призначається 18% CPU0. Ймовірно, вам потрібно 85/15 ~ 5.67 = між 5 і 6 екземплярами B, щоб отримати один екземпляр A для насичення одного ядра. Хоча введення / виведення, якщо воно є, може перекосити ці значення.
LSerni
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.