このUnixコマンドを最適化するには?

このUnixコマンドを最適化するには?

次のコマンドは結果を出力するのに約10分かかります。

find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
    awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head

パフォーマンスをどのように向上させることができますか?

答え1

これはすでにかなり最適化されています。詳細を知らないと、ボトルネックが何であるかを知ることは困難です。

  • ストレージタイプ(HD、SSD、ネットワーク、RAID)
  • 一致するファイルの数と平均サイズ
  • ディレクトリやその他の不一致ファイルの数
  • 1行あたりのフィールド数
  • 平均行長

どんな状況でもできること:

  • /がサポートしている場合はor-print | xargsに変更してください-exec cmd {} +。これは間違っているだけでなく、どの文字が空白なのかを調べるために文字をデコードし、高価な引用を行う必要があるため、コストがかかります。-print0 | xargs -r0findxargs-print | xargsxargs
  • ロケールをC(export LC_ALL=C)に変更しました。ここに含まれるすべての文字(|ファイルの内容の10進数、ファイル名のラテン文字、ピリオド、アンダースコアを含む)は移植可能な文字セットの一部であるため、文字セットがUTF-8または他のマルチバイト文字の場合は、C言語のシングルバイト文字セットこの言語に切り替えると、find多くの作業が節約されますawk
  • 部品をawk次のように単純化しますawk -F "|" '$14 == "20160920100643" && $22 == "567094398953"'
  • 出力をにパイプしているので、出力バッファリングを無効にして、できるだけ早くこれらの10行を出力できますhead。またはawk経由で使用できます。またはinを追加することもできます。gawkmawkfflush()if (++n == 10) exitawk

要約する:

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -exec zcat {} + |
  awk -F "|" '$14 == "20160920100643" && $22 == "567094398953" {
    print; if (++n == 10) exit}')

CPUがボトルネックを引き起こしている場合は、マルチコアGNUシステムで次のことを試すことができます。

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -print0 |
  xargs -r0P 4 -n 100 sh -c '
    zcat "$@" | 
      awk -F "|" "\$14 == "20160920100643" && \$22 == "567094398953" {
        print; fflush()}"' sh | head)

zcat | awk100ファイルのバッチで4つのジョブを並列に実行します。

タイムスタンプの場合は、以前20160920100643に最後に変更されたファイルを除外できます。 GNUまたはBSDのfind場合-newermt '2016-09-20 10:06:42'

行にフィールド数が多い場合は、行を分割して余りにもawk多くのフィールドを割り当てると$n不利益になります。最初の22フィールドのみを考慮するアプローチを使用すると、スピードを上げることができます。

grep -E '^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)'

awkコマンドより 。 GNUを使用して並列方式では最初にライン出力にオプションをgrep追加するか、非並列方式では10回一致後に停止します。--line-buffered-m 10

要約すると、CPUにボトルネックがあり、システムに4つ以上のCPUコア、400以上のmuc *ファイルがあり、GNUシステム(通常はgrepGNUよりはるかに高速ですawk)を使用しています。

(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -newermt '2016-09-20 10:06:42' -print0 |
  xargs -r0P 4 -n 100 sh -c '
    zcat "$@" | 
      grep --line-buffered -E \
        "^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)"
  ' sh | head)

並列アプローチでは、互いに混合されたコマンド出力を得ることができますgrep(ラインバッファリングを使用し、数キロバイト未満のラインを提供してもライン境界は維持する必要があります)。

答え2

@Stéphane Chazelasの答えは、コマンドパイプラインを最適化する方法に関する多くの詳細を提供します。

find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
    awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head

私はあなたが最も時間を費やす場所を実際に測定するための別のアプローチを提案します。時間をどこに書いているかを見つけたら、その時間で何をするかを決定できます。実行時間を10分向上させるには、2秒かかるステップを最適化することはほとんど役に立ちません。

コマンドパイプラインを見てみると、次の3つの点に注目しました。

  1. find .- ディレクトリ構造はどのように見えますか?各ディレクトリにはいくつのファイルがありますか?ディレクトリがコマンドを実行しているシステムにローカルですか?リモートファイルシステムは次のとおりです。たくさんゆっくり。
  2. -name "muc*_*_20160920_*.unl*"- ディレクトリ構造内のすべてのファイル名はどのくらい近いですか?それらはすべて名前に「近く、」一致しにくく/ CPU集約的ですか。なぜならすべてディレクトリツリーのファイル名をディスクから読み取ってパターンと比較する必要があります。
  3. xargs zcat- 私の考えでは、これは特に上記の問題とそれ自体xargsに比べてパフォーマンスの問題がそれほど大きくないようです。ファイル名が10,000個であっても10,000,000個であっても、名前を渡して解析するのにかかる時間は、ファイル名と比較してほとんど無視できます。findzcat発見する名前を付けて、すべてのファイル自体を開いて抽出します。ファイルのサイズはどのくらいですか?完全に解凍するからすべてファイル名パターンと一致するファイルfind

主なパフォーマンス問題が何であるかをどのように判断しますか?パイプラインで各コマンドのパフォーマンスを測定します。 (望むよりhttps://stackoverflow.com/questions/13294554/how-to-use-gnu-time-with-pipeline完全なパイプラインタイミングの詳細。 )次のコマンドを実行して、各ステップがパイプライン全体の処理時間に貢献する時間を確認できます。

/usr/bin/time find .- ディレクトリツリーの実行にかかる時間を通知します。速度が遅い場合は、より良いストレージシステムが必要です。 ファイルシステムキャッシュフラッシュ最悪の測定のためにタイミングを合わせる前に、タイミングを再実行して、キャッシュがパフォーマンスfindにどれだけ影響しているかを確認してください。ディレクトリがローカルでない場合は、ファイルを含む物理システムでコマンドを実行してみてください。

/usr/bin/time find . -name "muc*_*_20160920_*.unl*"- ファイル名をパターンマッチングするのにかかる時間を示します。ファイルシステムキャッシュを再フラッシュして2回実行します。

/usr/bin/time bash -c "find . -name 'muc*_*_20160920_*.unl*' | xargs zcat > /dev/null"- 私はこれがパイプラインの長いランタイムの主なコンポーネントだと思います。これが問題なら、zcatStéphane Chazelasの答えに従ってコマンドを並列化するのがおそらく最善の答えでしょう。

最も時間がかかる場所が見つかるまで、元のコマンドパイプラインの手順をテスト中のパイプラインに追加し続けます。もう一度これがzcatステップかどうか疑われます。もしそうなら、zcat@Stéphane Chazelasが投稿した並列化が役に立ちます。

並列化はzcat役に立たないかもしれません。ダメージパフォーマンスが低下し、処理速度が遅くなります。zcat一度に1つだけ実行されるため、IOは良いストリーミングモードになる可能性が高く、ディスクの検索を最小限に抑えます。複数のzcatプロセスが同時に実行されると、ディスクヘッドがブラウズする必要があり、先読み操作の効率が低下するため、IO操作が競合し、実際に処理が遅くなる可能性があります。

このzcatステップが主要なパフォーマンスボトルネックであり、zcat一度に複数のプロセスを実行しても役に立たない場合、または実際に遅くなる場合は、パイプラインはIOバインドされているため、より高速なストレージを使用して問題を解決する必要があります。

繰り返しますが、ディレクトリがコマンドパイプラインが実行されているコンピュータのローカルではない場合は、ファイルシステムが実際に存在するコンピュータで実行してみてください。

答え3

コメントで指摘したようにジグレフこれらのタスクのためのより良い選択グローバルスター次のように許可される**オプションall path inside the directory except hidden

shopt -s globstar
zgrep -m 10 '^\([^|]*|\)\{13\}20160920100643|\([^|]*|\)\{7\}567094398953' ./**muc*_*_20160920_*.unl*
shopt -u globstar

答え4

指摘したように、追加の詳細がなければ正解を提供することは不可能です。

locate -0 -b -r '^muc.*_.*_20160920_.*.unl.*gz' | 
   xargs -0  zcat |
   awk -F "|" '$14=="20160920100643" && $22=="567094398953"'| head
  • **1:検索(使用可能な場合)はorよりはるかに高速ですfind。使用される正規表現を調整する必要があります...

  • 2および3:PO用フィルタ

@rudmeierが賢明に指摘したようにlocate。 (たとえば、ほとんどのLinuxシステムでは、検索は毎日更新されるため、今日作成されたファイルは見つかりません。)

しかし、検索が可能であれば、これは非常に印象的なスピードアップをもたらします。

time ...購入注文がさまざまなソリューションを提供できる場合は興味深いでしょう。

関連情報