ファイル1..64はそれぞれ160MBで、RAMディスクに保存されます。
によって作成された:
seq 120 | parallel -k 'seq {}0000000 {}9999999 | fmt -30' | head -c 10G > 10G
parallel --pipepart --block -1 -a 10G -j 64 'cat > {#}'
nocat
:
#!/bin/bash
export LC_ALL=C
sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 1; sort ) < 1) \
<((rm 2; sort ) < 2) ) \
<(sort -m \
<((rm 3; sort ) < 3) \
<((rm 4; sort ) < 4) ) ) \
<(sort -m \
<(sort -m \
<((rm 5; sort ) < 5) \
<((rm 6; sort ) < 6) ) \
<(sort -m \
<((rm 7; sort ) < 7) \
<((rm 8; sort ) < 8) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 9; sort ) < 9) \
<((rm 10; sort ) < 10) ) \
<(sort -m \
<((rm 11; sort ) < 11) \
<((rm 12; sort ) < 12) ) ) \
<(sort -m \
<(sort -m \
<((rm 13; sort ) < 13) \
<((rm 14; sort ) < 14) ) \
<(sort -m \
<((rm 15; sort ) < 15) \
<((rm 16; sort ) < 16) ) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 17; sort ) < 17) \
<((rm 18; sort ) < 18) ) \
<(sort -m \
<((rm 19; sort ) < 19) \
<((rm 20; sort ) < 20) ) ) \
<(sort -m \
<(sort -m \
<((rm 21; sort ) < 21) \
<((rm 22; sort ) < 22) ) \
<(sort -m \
<((rm 23; sort ) < 23) \
<((rm 24; sort ) < 24) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 25; sort ) < 25) \
<((rm 26; sort ) < 26) ) \
<(sort -m \
<((rm 27; sort ) < 27) \
<((rm 28; sort ) < 28) ) ) \
<(sort -m \
<(sort -m \
<((rm 29; sort ) < 29) \
<((rm 30; sort ) < 30) ) \
<(sort -m \
<((rm 31; sort ) < 31) \
<((rm 32; sort ) < 32) ) ) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 33; sort ) < 33) \
<((rm 34; sort ) < 34) ) \
<(sort -m \
<((rm 35; sort ) < 35) \
<((rm 36; sort ) < 36) ) ) \
<(sort -m \
<(sort -m \
<((rm 37; sort ) < 37) \
<((rm 38; sort ) < 38) ) \
<(sort -m \
<((rm 39; sort ) < 39) \
<((rm 40; sort ) < 40) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 41; sort ) < 41) \
<((rm 42; sort ) < 42) ) \
<(sort -m \
<((rm 43; sort ) < 43) \
<((rm 44; sort ) < 44) ) ) \
<(sort -m \
<(sort -m \
<((rm 45; sort ) < 45) \
<((rm 46; sort ) < 46) ) \
<(sort -m \
<((rm 47; sort ) < 47) \
<((rm 48; sort ) < 48) ) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 49; sort ) < 49) \
<((rm 50; sort ) < 50) ) \
<(sort -m \
<((rm 51; sort ) < 51) \
<((rm 52; sort ) < 52) ) ) \
<(sort -m \
<(sort -m \
<((rm 53; sort ) < 53) \
<((rm 54; sort ) < 54) ) \
<(sort -m \
<((rm 55; sort ) < 55) \
<((rm 56; sort ) < 56) ) ) ) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 57; sort ) < 57) \
<((rm 58; sort ) < 58) ) \
<(sort -m \
<((rm 59; sort ) < 59) \
<((rm 60; sort ) < 60) ) ) \
<(sort -m \
<(sort -m \
<((rm 61; sort ) < 61) \
<((rm 62; sort ) < 62) ) \
<(sort -m \
<((rm 63; sort ) < 63) \
<((rm 64; sort ) < 64) ) ) ) ) ) |
md5sum
withcat
:
#!/bin/bash
export LC_ALL=C
sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 1; sort ) < 1) \
<((rm 2; sort ) < 2) | cat) \
<(sort -m \
<((rm 3; sort ) < 3) \
<((rm 4; sort ) < 4) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 5; sort ) < 5) \
<((rm 6; sort ) < 6) | cat) \
<(sort -m \
<((rm 7; sort ) < 7) \
<((rm 8; sort ) < 8) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 9; sort ) < 9) \
<((rm 10; sort ) < 10) | cat) \
<(sort -m \
<((rm 11; sort ) < 11) \
<((rm 12; sort ) < 12) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 13; sort ) < 13) \
<((rm 14; sort ) < 14) | cat) \
<(sort -m \
<((rm 15; sort ) < 15) \
<((rm 16; sort ) < 16) | cat) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 17; sort ) < 17) \
<((rm 18; sort ) < 18) | cat) \
<(sort -m \
<((rm 19; sort ) < 19) \
<((rm 20; sort ) < 20) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 21; sort ) < 21) \
<((rm 22; sort ) < 22) | cat) \
<(sort -m \
<((rm 23; sort ) < 23) \
<((rm 24; sort ) < 24) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 25; sort ) < 25) \
<((rm 26; sort ) < 26) | cat) \
<(sort -m \
<((rm 27; sort ) < 27) \
<((rm 28; sort ) < 28) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 29; sort ) < 29) \
<((rm 30; sort ) < 30) | cat) \
<(sort -m \
<((rm 31; sort ) < 31) \
<((rm 32; sort ) < 32) | cat) | cat) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 33; sort ) < 33) \
<((rm 34; sort ) < 34) | cat) \
<(sort -m \
<((rm 35; sort ) < 35) \
<((rm 36; sort ) < 36) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 37; sort ) < 37) \
<((rm 38; sort ) < 38) | cat) \
<(sort -m \
<((rm 39; sort ) < 39) \
<((rm 40; sort ) < 40) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 41; sort ) < 41) \
<((rm 42; sort ) < 42) | cat) \
<(sort -m \
<((rm 43; sort ) < 43) \
<((rm 44; sort ) < 44) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 45; sort ) < 45) \
<((rm 46; sort ) < 46) | cat) \
<(sort -m \
<((rm 47; sort ) < 47) \
<((rm 48; sort ) < 48) | cat) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 49; sort ) < 49) \
<((rm 50; sort ) < 50) | cat) \
<(sort -m \
<((rm 51; sort ) < 51) \
<((rm 52; sort ) < 52) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 53; sort ) < 53) \
<((rm 54; sort ) < 54) | cat) \
<(sort -m \
<((rm 55; sort ) < 55) \
<((rm 56; sort ) < 56) | cat) | cat) | cat) \
<(sort -m \
<(sort -m \
<(sort -m \
<((rm 57; sort ) < 57) \
<((rm 58; sort ) < 58) | cat) \
<(sort -m \
<((rm 59; sort ) < 59) \
<((rm 60; sort ) < 60) | cat) | cat) \
<(sort -m \
<(sort -m \
<((rm 61; sort ) < 61) \
<((rm 62; sort ) < 62) | cat) \
<(sort -m \
<((rm 63; sort ) < 63) \
<((rm 64; sort ) < 64) | cat) | cat) | cat) | cat) | cat) | cat |
md5sum
唯一の違いは、withcat
すべてがsort -m
にパイプされていることですcat
。
「猫の役に立たない利用」 - 人々はあなたをwithcat
よりゆっくりと信じさせますnocat
。しかし、その逆は本当です。
$ time bash nocat
c933d81faea7b8dec8eb64ca0b044d74 -
real 3m40.854s
user 2m48.687s
sys 0m49.135s
$ time bash withcat
c933d81faea7b8dec8eb64ca0b044d74 -
real 2m21.812s
user 2m16.651s
sys 1m36.135s
このテストは64コアシステムで実行され、他の操作は実行されません。すべてがRAMにあります(したがってディスクが遅くなるわけではありません)。各テストは3回実行され、最良の時間は上に示されています。 3つのテストすべてが最高の時間から5秒以内に完了しました(それは偶然ではありませんでした)。
出力をパイプする方が速いのはなぜですかcat
?
編集する
グループ入力がcat
大きくなるのでしょうか?そして/またはsort
各ラインの出力をフラッシュしますか?
これをテストするために、次のことを試しました。
$ strace -ff sort -m <(sort 1) <(sort 2) 2>fromsort | cat >/dev/null
$ strace -ff sort -m <(sort 1 | cat ) <(sort 2 | cat) 2>fromcat | cat >/dev/null
cat
大きなチャンクに分割すると、よりread
大きなチャンクが返されると予想されます。しかし、そうではありません:
$ grep -E 'read|write' fromsort |field 1,5|sort | uniq -c
1 openat(AT_FDCWD, 3
8 pread64(3, =
1 read(3, 3771
40989 read(3, 4096
2 read(3, 832
1 read(3, unknown
1 read(4, 0
1 read(4, 2241
40959 read(4, 4096
1 write(1, 1916
81949 write(1, 4096
$ grep -E 'read|write' fromcat |field 1,5|sort | uniq -c
1 openat(AT_FDCWD, 3
8 pread64(3, =
1 read(3, 3771
40989 read(3, 4096
2 read(3, 832
1 read(3, unknown
1 read(4, 2241
40959 read(4, 4096
1 read(4, unknown
1 write(1, 1916
81949 write(1, 4096
どちらの場合も、read
合計はwrite
4Kです。
(ところで、sort
するパイプではなくファイルから読み込む場合(より多く)、大きなチャンクを読みますが、ここではそうではありません。
編集2
上記の目的は、追加がcat
必ずしも役に立たないわけではないことを示し、これが本当である理由を見つけることです。
目標はデータをソートすることではありません。
しかし目標なら以前はsort
データを並べ替えるには、組み込みデータを使用するとどうなりますか--parallel
?
64コアマシンでデフォルトで使用されているsort
ようです。 CPUの最大800%を使用していることを示しています。 64個のコアを使用するように強制できます。--parallel 8
top
--parallel 64
$ time sort {1..64} | md5sum
real 9m4.005s
user 29m56.454s
sys 5m49.560s
$ time sort --parallel 64 {1..64} | md5sum
real 6m50.332s
user 35m55.040s
sys 11m37.609s
したがって、GNUソートは--parallel
上記のソートよりもはるかに遅くなります。これで上記のコンテンツを使用できますparsort
。http://git.savannah.gnu.org/cgit/parallel.git/tree/src/parsort
答え1
これは決して「猫の無駄な行動」ではない。
some_command | cat | some_command
これは伝統的な意味では「猫の目の前の路地」ではなく、通常はエンクロージャの無知から発生します。代わりに猫のダイナミズムを持って何かをしようとする意図的な試みのように見えます。この場合はキャッシュだと思います。
私の2番目の考え
読み取りと書き込みのサイズに違いがなくても、検出できないものが機能する可能性があります。
まず(非常に重要):ソートされていない配列よりもソートされた配列を処理する方が速いのはなぜですか?。 CPUがこのタスクを処理する順序を変更すると、タイミングが変わる可能性があります。中断(および他のプロセスに切り替える)なしでcat
各実行時間を長くすることに成功すると、sort
これはCPUの分岐予測に大きな影響を与え、時間が長くなったり短くなったりする可能性があります。
次に、読み取り数とサイズが影響を受けない場合でも、ジョブを一時停止(ブロック)する必要がある回数は異なる場合があります。これはそれ自体でオーバーヘッドを増減することができます。したがって、読み出しと書き込みのサイズが同じであっても、(キャッシュ)階層は、1時間当たりの読み書きcat
数を減らすことができる。read()
write()
Catはソートを長く待つことで、中断することなくより多くの作業を実行し、各プロセスがブロックされる回数を減らすことができます。これは検出するのが難しいでしょう。
私の最初の考え
私の期待は、2つのバージョンを独自のスクリプトに配置し、strace -f
それぞれで実行すると、catを使用した例では、読み込みまたは書き込み呼び出しが少なくなることです。少なくともcat
。sort
実際には、私はそれがread()
十分に大きなブロックにありますが、write()
単一の行にしかないことを望みます。これは、それ自体が配管されるように設計されていないことを意味します。
ロックタックが指摘したように、彼の答え、catは128KBチャンクで読みます(ねえ)しかし、パイプは通常64KBだけバッファリングします。私の考えが正しい場合は、キャットがread()
完了するのを待っている間、停止せずに書き込むことsort
ができる大きな(128 + 64KB)バッファを書き込み操作に提供します。回復すると、次の書き込みに渡すcat
データが多くなります(sort
単一の書き込みで送信されたものよりもはるかに多い)sort
。だから次の人はsort
止まることなくその内容をたくさん読むことができます。
私も疑う次へ追加最も近いファイル階層は、cat
パフォーマンスにほとんどまたはまったく影響しません。これらのファイルはすでにRAMディスクにキャッシュされています。ただし、呼び出し間のレイヤーはsort
バッファーとして機能するため、その数を減らす必要があります。これは実際に「catの役に立たない使用」、つまりcatを使用してファイルを読み取る状況です。これは次の形式です。
cat some_file | some_command
興味深い実験
パイプのバッファサイズを増やして同じ効果が得られるかどうかを知りたいです。正しいプログラミング言語(シェルではない)を使用して同じパイプを設定する場合。たとえば、Cでは、、、を使用してパイプを作成し、各パイプを最初に呼び出してバッファサイズを増やすことができます(参照pipe()
:dup2()
fork()
exec()
ioctl()
パイプ容量)
答え2
私の考えでは、catを使用すると、各コマンドのスループットが制限され、コマンドが並列に高速で実行されるようです。
cat
データの読み取り128KBチャンク。あなたのテストを再現することができないので、あなたの使い方を変えて、私が正しいかどうかを証明cat
できますか?dd
dd status=none bs=128K
同じ効果があるはずですcat
。チャンクサイズを増減することで結果を比較してみてください。