przy użyciu równoległego przetwarzania unikatowych plików wejściowych do unikatowych plików wyjściowych


18

Mam problem ze skryptem powłoki, w którym dostaję katalog pełen plików wejściowych (każdy plik zawiera wiele wierszy wejściowych) i muszę przetwarzać je osobno, przekierowując każde z ich wyników do unikalnego pliku (aka, plik_1.input potrzebuje do przechwycenia w pliku_1.output itd.).

Przed równolegle , po prostu iterowałbym każdy plik w katalogu i wykonywał moje polecenie, wykonując jakąś technikę licznika / liczenia, aby nie przytłoczyć procesorów (zakładając, że każdy proces miał stały czas działania). Wiem jednak, że nie zawsze tak będzie, więc użycie rozwiązania „równoległego” wydaje się najlepszym sposobem na uzyskanie wielowątkowości skryptu powłoki bez pisania niestandardowego kodu.

Chociaż zastanawiałem się nad niektórymi sposobami równoległego przetwarzania w celu przetworzenia każdego z tych plików (i umożliwiania wydajnego zarządzania rdzeniami), wszystkie wydają się hackerskie. Mam, jak sądzę, dość łatwą w użyciu skrzynkę, więc wolałbym zachować ją tak czystą, jak to możliwe (i nic w równoległych przykładach nie wydaje się być moim problemem.

Każda pomoc będzie mile widziana!

przykład katalogu wejściowego:

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

Scenariusz:

> cat proces_script.sh
#!/bin/sh

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

Aktualizacja : po przeczytaniu odpowiedzi Ole poniżej udało mi się zebrać brakujące elementy do mojej równoległej implementacji. Chociaż jego odpowiedź jest świetna, oto moje dodatkowe badania i notatki, które wziąłem:

Zamiast uruchomić cały proces, pomyślałem, że zacznę od dowodu koncepcji, aby sprawdzić jego rozwiązanie w moim środowisku. Zobacz moje dwie różne implementacje (i uwagi):

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

Używa find (nie ls, co może powodować problemy), aby znaleźć wszystkie odpowiednie pliki w moim katalogu plików wejściowych, a następnie przekierowuje ich zawartość do osobnego katalogu i pliku. Mój problem z góry polegał na czytaniu i przekierowywaniu (sam skrypt był prosty), więc zastąpienie go kotem było dobrym dowodem koncepcji.

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

To drugie rozwiązanie wykorzystuje paradygmat zmiennych wejściowych równoległych do odczytu plików, jednak dla nowicjuszy było to znacznie bardziej mylące. Dla mnie użycie find a pipe potwierdziło moje potrzeby.

Odpowiedzi:


27

GNU Parallel jest przeznaczony do tego rodzaju zadań:

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

lub:

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

Będzie uruchamiał jedno zadanie na rdzeń procesora.

Możesz zainstalować GNU Parallel po prostu przez:

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

Obejrzyj wideo wprowadzające do GNU Parallel, aby dowiedzieć się więcej: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


Świetna odpowiedź (i główne punkty za przeczytanie mojej prośby o użycie równoległe).
J Jones

5

Standardowym sposobem na to jest ustawienie kolejki i odrodzenie dowolnej liczby pracowników, którzy wiedzą, jak wyciągnąć coś z kolejki i przetworzyć. Do komunikacji między tymi procesami można użyć fifo (aka o nazwie potok).

Poniżej znajduje się naiwny przykład pokazujący tę koncepcję.

Prosty skrypt kolejki:

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

I pracownik:

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

process_file może być zdefiniowane gdzieś u twojego pracownika i może zrobić wszystko, czego potrzebujesz.

Po uzyskaniu tych dwóch elementów możesz mieć prosty monitor, który uruchamia proces kolejki i dowolną liczbę procesów roboczych.

Skrypt monitorowania:

#!/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

Masz to. Jeśli faktycznie to zrobisz, lepiej ustawić fifo na monitorze i przekazać ścieżkę zarówno do kolejki, jak i pracowników, aby nie były one połączone i nie utknęły w określonej lokalizacji dla fifo. Ustawiłem to w odpowiedzi, aby było jasne, że używasz tego, co czytasz.


W jaki sposób monitor jest wystarczająco inteligentny, aby wstrzymać odradzanie się nowych pracowników, dopóki nie zakończy się następny (czyli, gdzie $ kiedykolwiek się zmniejszam)? ---- Odpowiadając na moją własną edycję, pracownicy nigdy nie odchodzą, po prostu przetwarzają pliki, dopóki całe przetwarzanie nie zostanie wyczerpane (stąd też pętla while w „procesorach”).
J Jones

Jaka jest wydajność wiersza „monitor_workers” na końcu skryptu monitorowania?
J Jones

@JJones - monitor_workersjest jak process_file- to funkcja, która robi, co chcesz. O monitorze - miałeś rację; powinien zapisać stawki swoich pracowników (aby mógł wysłać sygnał zabicia), a licznik należy zwiększyć, gdy uruchomi pracownika. Zredagowałem odpowiedź, aby to uwzględnić.
Shawn J. Goff,

Naprawdę doceniam twoją pracę, ale myślę, że powinieneś używać GNU parallel. Myślę, że to Twój pomysł, w pełni zrealizowany.
motobói

5

Inny przykład:

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

Znalazłem inne przykłady niepotrzebnie złożone, gdy w większości przypadków powyższe jest tym, czego mogłeś szukać.


4

Jest powszechnie dostępnym narzędziem, które może wykonywać paralelizację. Marka GNU i kilka innych ma -jopcję wykonywania kompilacji równoległych.

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

Uruchom w maketen sposób (zakładam, że twoje nazwy plików nie zawierają żadnych znaków specjalnych, makenie jest z nimi dobra):

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

imho to najmądrzejsze rozwiązanie :)
h4unt3r

3

Aby wykonać tę samą komendę na dużym zestawie plików w bieżącym katalogu:

#!/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

Spowoduje to uruchomienie customScriptkażdego txtpliku, umieszczając dane wyjściowe w outtxtplikach. Zmień według potrzeb. Kluczem do tego, aby to zadziałało, jest przetwarzanie sygnału za pomocą SIGUSR1, aby proces potomny mógł poinformować proces nadrzędny, że jest on wykonywany. Użycie SIGCHLD nie będzie działać, ponieważ większość instrukcji w skrypcie generuje sygnały SIGCHLD do skryptu powłoki. Próbowałem tego, zastępując twoje polecenie sleep 1, program wykorzystał 0,28s procesora użytkownika i 0,14s procesora systemowego; dotyczyło to tylko około 400 plików.


W jaki sposób funkcja „wait” jest wystarczająco inteligentna, aby pobrać ten sam plik, który jest obecnie iterowany i ponownie wprowadzić instrukcję „if” rodzeństwa?
J Jones

To nie waitjest wystarczająco „inteligentne”; ale wróci po otrzymaniu SIGUSR1sygnału. Dziecko / pracownik wysyła a SIGUSR1do rodzica, który jest przechwytywany ( trap), i dekrementuje $worker( trapklauzula) i wraca nienormalnie z wait, umożliwiając wykonanie if [ $worker -lt $num_workers ]klauzuli.
Arcege

0

Lub po prostu użyj xargs -P, bez potrzeby instalowania dodatkowego oprogramowania:

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

Trochę wyjaśnienia dla opcji:

  • -I'XXX' ustawia ciąg, który zostanie zastąpiony w szablonie poleceń nazwą pliku
  • -P4 uruchomi 4 procesy równolegle
  • -n1 umieści tylko jeden plik na wykonanie, mimo że znaleziono dwa XXX
  • -print0i -0współpracują ze sobą, pozwalając na stosowanie znaków specjalnych (takich jak białe znaki) w nazwach plików
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.