benzersiz çıktı dosyalarını benzersiz çıktı dosyalarına işlemek için paralel kullanma


18

Ben giriş dosyaları (birçok giriş satırı içeren her dosya) dolu bir dizin verildi bir kabuk komut dosyası sorunum var ve ben her biri kendi çıktıları benzersiz bir dosya (aka, file_1.input ihtiyaçlarını yeniden yönlendirerek, ayrı ayrı işlemek gerekir file_1.output vb. içinde yakalanacaktır).

Önceden paralel olarak , işlemcileri boğmamak için bir çeşit zamanlayıcı / sayma tekniği yaparken (her işlemin sabit bir çalışma zamanı olduğunu varsayarak) dizindeki her dosya üzerinde tekrarlar ve komutumu gerçekleştiririm. Ancak, her zaman böyle olmayacağını biliyorum, bu yüzden "paralel" gibi bir çözüm kullanmak özel kod yazmadan kabuk komut dosyası çok iş parçacıklı almak için en iyi yol gibi görünüyor.

Bu dosyaların her birini işlemek için paralel kırbaçlamak için bazı yollar düşünürken (ve çekirdeklerimi verimli bir şekilde yönetmeme izin verirken), hepsi hacky görünüyor. Oldukça kolay bir kullanım durumu olduğunu düşündüğüm şey var, bu yüzden mümkün olduğunca temiz tutmayı tercih ederim (ve paralel örneklerde hiçbir şey benim sorunum olarak atlanmıyor gibi görünüyor.

Herhangi bir yardım mutluluk duyacağız!

giriş dizini örneği:

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

Senaryo:

> cat proces_script.sh
#!/bin/sh

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

Güncelleme : Ole'nin aşağıdaki cevabını okuduktan sonra, kendi paralel uygulamam için eksik parçaları bir araya getirebildim. Cevabı harika olsa da, ek araştırmalarım ve aldığım notlar:

Tüm sürecimi yürütmek yerine, çözümünü çevremde kanıtlamak için bir konsept emri belgesi ile başlamayı düşündüm. İki farklı uygulamama (ve notuma) bakın:

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

Giriş dosyaları dizinimdeki tüm geçerli dosyaları bulmak için find (ls değil, sorunlara neden olabilir) kullanır ve ardından içeriklerini ayrı bir dizine ve dosyaya yeniden yönlendirir. Yukarıdaki sorunum okuma ve yönlendirme (asıl komut dosyası basitti), bu yüzden komut dosyasını kedi ile değiştirmek kavramın güzel bir kanıtıydı.

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

Bu ikinci çözüm, dosyaları okumak için parallel'in giriş değişkeni paradigmasını kullanır, ancak bir acemi için bu çok daha kafa karıştırıcıydı. Benim için, bulmak bir ve boru kullanarak benim ihtiyaçlarını iyi karşıladı.

Yanıtlar:


27

GNU Parallel bu tür görevler için tasarlanmıştır:

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

veya:

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

CPU çekirdeği başına bir iş çalıştıracaktır.

GNU Parallel'ı aşağıdaki yollarla kurabilirsiniz:

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

Daha fazla bilgi edinmek için GNU Parallel için tanıtım videolarını izleyin: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


Büyük cevap (ve paralel kullanma isteğimi okumak için önemli noktalar).
J Jones

5

Bunu yapmanın standart yolu, bir kuyruk oluşturmak ve kuyruktan bir şey almayı ve işlemeyi bilen çok sayıda işçiyi ortaya çıkarmaktır. Bu işlemler arasında iletişim kurmak için bir fifo (aka pipe adı verilir) kullanabilirsiniz.

Aşağıda konsepti göstermek için naif bir örnek verilmiştir.

Basit bir kuyruk komut dosyası:

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

Ve bir işçi:

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

process_file çalışanınızda bir yerde tanımlanabilir ve yapmanız gereken her şeyi yapabilir.

Bu iki parçaya sahip olduğunuzda, kuyruk işlemini ve herhangi bir sayıda işçi işlemini başlatan basit bir monitöre sahip olabilirsiniz.

İzleme komut dosyası:

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

İşte aldın. Bunu gerçekten yaparsanız, monitördeki fifo'yu ayarlamak ve hem sıraya hem de işçilere giden yolu geçmek daha iyidir, bu nedenle bunlar birleştirilmez ve fifo için belirli bir yere yapışmaz. Bunu cevabın içinde bu şekilde ayarladım, böylece okurken kullandığınız şey açıktır.


Monitör, bir sonraki iş bitene kadar yeni işçilerde yumurtlamayı duraklatacak kadar akıllıdır (aka, $ i hiç azaltılır)? ---- Kendi düzenlememi yanıtlayan işçiler asla gitmezler, sadece tüm işlemler bitene kadar dosyaları işlerler (bu nedenle 'işlemciler' içindeki while döngüsü).
J Jones,

Monitör komut dosyasının sonundaki "monitor_workers" satırı ne yapıyor?
J Jones

@JJones - monitor_workersaynen process_file- istediğiniz her şeyi yapan bir işlevdir. Monitör hakkında - haklıydınız; işçilerinin pid'lerini kaydetmelidir (böylece bir öldürme sinyali gönderebilir) ve bir işçi çalıştırdığında sayacın artırılması gerekir. Cevabı buna dahil etmek için düzenledim.
Shawn J. Goff

Çalışmanızı gerçekten takdir ediyorum, ancak bence GNU'ları kullanmalısınız parallel. Bence bu senin fikrin, tamamen uygulanmış.
motobói

5

Başka bir örnek:

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

Diğer örnekleri gereksiz yere karmaşık buldum, çoğu durumda yukarıdakiler aradığınız şey olduğunda.


4

Paralelleştirme yapabilen yaygın olarak kullanılan bir araç yapmaktır. GNU markası ve birkaç diğer -jmodelin paralel yapı oluşturma seçeneği vardır.

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

makeBu şekilde çalıştırın (Dosya adlarınızın herhangi bir özel karakter içermediğini varsayıyorum, bunlarla makeiyi değil):

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

imho bu en zekice bir çözüm :)
h4unt3r

3

Bu, geçerli dizindeki büyük bir dosya kümesinde aynı komutu gerçekleştirmek içindir:

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

Bu , çıktıyı dosyalara koyarak customScripther txtdosyada çalışır outtxt. İhtiyacınız olanı değiştirin. Bunu çalıştırmanın anahtarı, SIGUSR1 kullanan sinyal işlemedir, böylece alt süreç, üst sürecin bunun yapıldığını bilmesini sağlayabilir. Koddaki ifadelerin çoğu kabuk komut dosyasına SIGCHLD sinyalleri üreteceğinden SIGCHLD kullanımı çalışmaz. Bu, komutunuzu değiştirerek denedim sleep 1, program 0.28s kullanıcı cpu ve 0.14s sistem cpu kullandı; bu sadece yaklaşık 400 dosyadaydı.


'Bekle' şu anda yinelenmekte olan aynı dosyayı alacak ve kardeş "if" deyimini tekrar girecek kadar akıllıdır?
J Jones

O değil wait'akıllı' yeter olduğunu; ancak SIGUSR1sinyali aldıktan sonra geri dönecektir . Çocuk / işçi SIGUSR1yakalanan ( trap) ve ebeveyne bir cümle gönderir ve fıkralar $worker( trapfıkra) ile anormal olarak geri döner waitve if [ $worker -lt $num_workers ]fıkra yürürlüğe girer.
Arcege

0

Veya sadece kullanın xargs -P, ek yazılım yüklemeye gerek yok:

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

Seçenekler için biraz açıklama:

  • -I'XXX' komut şablonunda değiştirilecek dizeyi dosya adıyla ayarlar
  • -P4 paralel olarak 4 işlem gerçekleştirecek
  • -n1 iki XXX bulunsa bile yürütme başına yalnızca bir dosya koyacaktır
  • -print0ve -0birlikte çalışarak dosya adlarında özel karakterlerin (boşluk gibi) olmasını sağlayın
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.