Membuat aliran output tunggal dari tiga aliran lain yang diproduksi secara paralel

10

Saya memiliki tiga jenis data yang dalam format berbeda; untuk setiap tipe data, ada skrip Python yang mengubahnya menjadi satu format tunggal.

Skrip Python ini lambat dan terikat CPU (ke satu inti pada mesin multi-inti), jadi saya ingin menjalankan tiga contoh - satu untuk setiap tipe data - dan menggabungkan output mereka untuk meneruskannya sort. Pada dasarnya, setara dengan ini:

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

Tetapi dengan tiga skrip berjalan secara paralel.

Saya menemukan pertanyaan ini di mana GNU splitdigunakan untuk membulatkan beberapa aliran stdout antara n contoh skrip yang menangani aliran.

Dari halaman manual split:

-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

Jadi r/Nperintah itu menyiratkan " tanpa garis pemisah ".

Berdasarkan ini, sepertinya solusi berikut harus layak:

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

Di mana choose_scriptini:

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

Sayangnya, saya melihat beberapa jalinan garis - dan banyak baris baru yang seharusnya tidak ada di sana.

Misalnya, jika saya mengganti skrip Python saya dengan beberapa skrip bash sederhana yang melakukan ini:

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

Saya melihat output ini:

1-8394

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

Ini menjengkelkan - berdasarkan pada ekstrak halaman manual yang saya tempel di atas, itu harus menjaga integritas garis.

Jelas itu berfungsi jika saya menghapus -uargumen, tetapi kemudian buffered dan saya akan kehabisan memori karena buffer output semua kecuali satu skrip.

Jika ada yang memiliki wawasan di sini, itu akan sangat dihargai. Saya keluar dari kedalaman saya di sini.

Cera
sumber
Beberapa orang di #bash on freenode menyarankan agar saya menelurkan ketiga proses dan melatarbelakanginya, menulis ke FD kustom, lalu mengulangi FD tersebut dan membaca baris untuk mereka, tetapi saya belum menemukan cara untuk membuatnya bisa diterapkan. Saya juga diberitahu untuk melihat coprocbuiltin di bash, meskipun saya tidak benar-benar melihat bagaimana itu berlaku.
Cera
1
Apakah Anda harus melakukannya tanpa file perantara? Tidak bisakah kamu melakukannya job1.py > file1 & job2.py > file 2 & job3.py > file3 ; wait ; sort -n file1 file2 file3?
angus

Jawaban:

2

Coba gunakan opsi -u paralel GNU.

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

Ini menjalankannya secara paralel, tanpa buffering keseluruhan proses.

flowblok
sumber
Aku agak bingung - adalah Xdi IXmengatakan -Ibahwa X akan bendera untuk mengganti, atau itu menerapkan -Xbendera, yang tampaknya juga memiliki makna yang relevan?
Cera
Hmph. Saya melakukan ini parallel -u -X ./handle_{}.sh ::: "1" "2" "3":, dan sayangnya saya masih melihat beberapa output mangling.
Cera
yang pertama: Anda juga dapat menggunakan parallel -u ./handle_{}.sh, tetapi saya lebih suka mengubahnya, karena kawat gigi juga memiliki arti menggabungkan perintah bersama (seperti dalam pertanyaan Anda).
flowblok
Tampaknya bekerja untuk saya, grep saya tidak mengambil masalah apa pun: pastie.org/5113187 (apakah Anda menggunakan skrip bash pengujian, atau skrip Python Anda yang sebenarnya?)
flowblok
Masalahnya adalah itu sebenarnya tidak melakukan apa pun secara paralel. Saya menggunakan skrip bash - pastie.org/5113225
Cera
2

Mencoba:

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

Jika handle_1.pymengambil nama file:

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

Anda tidak ingin output tercampur, jadi jangan gunakan -u.

Jika Anda ingin menjaga urutan (jadi semua keluaran handle_1 adalah sebelum handle_2 dan dengan demikian Anda mungkin bisa menghindari penyortiran):

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

Jika Anda tetap menginginkannya diurutkan, Anda dapat memparalelkan sortir dan memanfaatkan sort -m:

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

Set $ ​​TMPDIR ke dir yang cukup besar untuk menampung output.

Ole Tange
sumber
1
Saya ingin output 'dicampur' - Saya hanya ingin memastikan bahwa setiap baris dalam output akhir adalah satu baris dari salah satu subproses. Jika saya tidak mencampurnya, sistem akan kehabisan memori buffer stdout stream yang belum dicetak.
Cera
Dengan GNU Parallel Anda tidak akan kehabisan memori: Tidak buffer dalam memori. Menurut Anda mengapa itu buffer dalam memori?
Ole Tange
2

Mungkin saya kehilangan sesuatu, tetapi tidak bisakah Anda melakukannya:

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

Jika Anda ingin garis-garis dari setiap proses tidak diselingi, lebih mudah mungkin untuk memastikan bahwa proses itu sendiri menuliskannya secara penuh dan mungkin menonaktifkan buffering output karena writes ke pipa dijamin atom selama mereka tidak lebih besar dari PIPE_BUF. Misalnya, Anda bisa memastikan itu tidak menggunakan buffering à la stdiodan panggilan fflushatau apa pun yang setara pythonsetelah satu atau beberapa baris telah ditulis.

Jika Anda tidak dapat mengubah skrip python, Anda dapat melakukan:

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

(dengan GNU grep) atau:

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

(Lihat catatan dalam komentar di bawah ini jika apa perintah output bukan teks)

Dan lakukan:

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

Pilihan lain untuk menghindari 3 lbproses tersebut adalah memiliki tiga pipa ke satu perintah yang menggunakan select/ polluntuk melihat dari mana ada beberapa output yang berasal dan mengumpankannya ke sortberbasis garis, tetapi membutuhkan sedikit pemrograman.

Stéphane Chazelas
sumber
Anda perlu waitdi sana, saya pikir.
derobert
1
Tidak, kecuali beberapa program menutup stdout mereka sebelum keluar, karena pipa dan sort -nakan tetap sampai semua program yang memiliki fd terbuka di atasnya telah keluar.
Stéphane Chazelas
Memang, saya diuji, Anda benar.
derobert
Tidak, saya masih mendapatkan hasil yang rusak. Garis dicampur bersama dan disisipkan.
Cera
1
OK @Cerales, lihat jawaban saya yang diperbarui
Stéphane Chazelas
1

Jawaban Flowbok adalah solusi yang benar. Anehnya, output GNU parallelakan hancur jika itu output langsung ke file - tetapi tidak jika pergi ke tty.

Untungnya, script -ctersedia untuk meniru tty.

Masih ada tiga skrip:

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

Lalu ada file yang merangkum panggilan ke paralel:

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

Dan saya menyebutnya seperti ini:

script -c ./run_parallel > output

Garis-garis dalam output dicampur baris-demi-baris antara output dari skrip yang berbeda, tetapi mereka tidak mendapatkan rusak atau disisipkan pada baris yang diberikan.

Perilaku aneh dari parallel- Saya dapat mengajukan laporan bug.

Cera
sumber