web-dev-qa-db-ja.com

並行して生成された他の3つのストリームから単一の出力ストリームを作成する

異なる形式の3種類のデータがあります。データ型ごとに、それを単一の統一された形式に変換するPythonスクリプトがあります。

このPythonスクリプトは遅く、CPUバウンド(マルチコアマシンのシングルコア)なので、データ型ごとに1つずつ)、3つのインスタンスを実行してそれらを組み合わせます。 sortに渡すための出力。基本的に、これと同等です。

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

しかし、3つのスクリプトが並行して実行されています。

この質問 ここでGNU splitは、ストリームを処理するスクリプトのn個のインスタンス間でいくつかのstdoutストリームをラウンドロビンするために使用されていました。

分割されたmanページから:

-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引数を削除すると機能しますが、その後バッファリングされ、1つを除くすべてのスクリプトの出力をバッファリングするため、メモリが不足します。

誰かがここでいくつかの洞察を持っているなら、それは大いにありがたいです。私はここで私の深さを超えています。

10
Cera

GNU parallelの-uオプションを使用してみてください。

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

これにより、プロセス全体をバッファリングすることなく、それらを並行して実行します。

2
flowblok

試してみてください:

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を使用しないでください。

順序を維持したい場合(したがって、すべてのhandle_1出力はhandle_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を、出力を保持するのに十分な大きさのディレクトリに設定します。

2
Ole Tange

多分私は何かが足りないのですが、あなたはただすることができません:

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

各プロセスの行がインターリーブされないようにする場合は、プロセス自体がそれらを完全に書き込むようにし、パイプへのwritesがアトミックであることが保証されているため、出力バッファリングを無効にする方が簡単です。 PIPE_BUF。たとえば、次のことを確認できますdoes出力バッファリングàlastdioを使用し、1行または数行が書き込まれた後、fflushまたはpythonにある同等のものを呼び出します。

pythonスクリプトを変更できない場合は、次のことができます。

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

(with 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を使用する1つのコマンドへの3つのパイプを使用して、出力の送信元を確認し、それをsort行ベースにフィードすることですが、プログラミングが少し必要です。

2

Flowbokの答えは正しい解決策でした。奇妙なことに、GNU parallelの出力は、ファイルに直接出力される場合はマングルされますが、ttyに送られる場合はマングルされません。

幸運なことに、 script -cはttyを模倣するために利用できます。

まだ3つのスクリプトがあります:

#!/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からの奇妙な動作-バグレポートを提出する場合があります。

1
Cera