このシングルライナーをより速くする方法はありますか?

このシングルライナーをより速くする方法はありますか?

コンテキスト

私はそれぞれ約300Kの表形式で日付が指定された何千ものzipファイルを含むディレクトリを持っていますYYYYMMDD_hhmmss.zip。各zipファイルには約400個のxmlファイルがあり、各ファイルのサイズは約3Kです。

質問

zipファイルの日付範囲内で特定の文字列を検索して見つけることができるはずです。

現在(通常ですが)ソリューション

私は次の行を持っています

find /home/mydir/ -type f | sort | \
awk "/xml_20140207_000016.zip/,/xml_20140207_235938.zip/" | \
xargs -n 1 -P 10 zipgrep "my search string"

ポイントは

  1. 私の1000ファイルディレクトリのすべてのファイルを一覧表示する
  2. このファイルリストの並べ替え
  3. 指定された日付に基づいてファイル範囲を検索します(このawkコマンドは、最初の一致文字列の後の行と2番目の一致文字列の前の行のみを印刷します)
  4. 単一のファイルに対応する各結果行を次に渡します。zipgrep

質問

24コアシステムに10個のプロセスがあっても、このコードの1行は非常に遅く実行されます。zipgrep命令のために遅いと思いますが、改善方法を知るほどスマートではありません。これを行うべきかどうかはわかりませんが、同僚がこのスクリプトよりも速く実行するJavaツールを作成したので、少し困惑しています。できればこれを裏返したいです。それでは、この場合、このコマンドをより速くする方法を知っている人はいますか?それともどの部分を改善しますか?

答え1

簡単に改善できる区間が一つありますが、最も遅い区間ではありません。

find /home/mydir/ -type f | sort | \
awk "/xml_20140207_000016.zip/,/xml_20140207_235938.zip/"

これは最初にすべてのファイルをリストし、次にファイル名をソートし、興味のあるファイルを抽出するため、少し無駄です。findソートを開始する前に、コマンドを完了する必要があります。

まず、関心のあるファイルのみを一覧表示するか、少なくとも可能な最小の親セットを一覧表示する方が高速です。 names に対してよりきめ細かいフィルタが必要な場合は、findawk にパイプするがソートしないでください。 awkおよび他の行ごとのフィルタは1行ずつ処理できますが、ソートには完全な入力が必要です。

find /home/mydir/ -name 'xml_20140207_??????.zip' -type f | \
awk 'match($0, /_[0-9]*.zip$/) &&
     (time = substr($0, RSTART+1, RLENGTH-5)) &&
     time >= 16 && time <= 235938' |
xargs -n 1 -P 10 zipgrep "my search string"

最も明らかに、次善策の部分はzipgrepです。シェルプログラミングの制限によりパフォーマンスを向上させる簡単な方法はありません。 zipgrepスクリプトは、アーカイブのファイル名をリストし、grep各ファイルの内容を1つずつ呼び出す方法で機能します。これは、zipアーカイブのすべてのファイルが引き続き解析されることを意味します。 Javaプログラム(またはPerl、Python、Rubyなど)はファイルを一度だけ処理してこれを防ぐことができます。

シェルプログラミングに固執するには、zipgrepを使用する代わりに各zipをマウントしてみることができます。

… | xargs -n1 -P2 sh -c '
    mkdir "mnt$$-$1";
    fuse-zip "$1" "mnt$$-$1";
    grep -R "$0" "mnt$$-$1"
    fusermount -u "mnt$$-$1"
' "my search string"

並列処理はあまり役に立ちません。ほとんどの設定では、制限要因はCPU時間ではなくディスクI / O帯域幅です。

ベンチマークしたことはありませんが、最大の改善分野は言語でzipgrepのより強力な実装を使用することだと思います。

答え2

いくつかの迅速な考え;

  • すべてのファイルが1つのディレクトリにある場合は削除できますfind
  • sortファイル名の規則は日付でソートされるため、対応するビットも必要ありません。
  • これらの2つの部分が解決されると、日付範囲がわかっている場合は、awkの代わりに単純なファイル名globを使用できます。たとえば(シェルがと仮定bash):

    • 日中のすべてのファイル

      echo xml_20140207_*.zip | xargs -n 1 -P 10 zipgrep "my search string"

    • 2014年2月7日または2月10日の15:00~18:00の間に生成されたファイル:

      echo xml_201402{07,10}_1{5..7}*.zip | xargs -n 1 -P 10 zipgrep "my search string"

答え3

ボトルネックが発生する場所は明確ではありません。ファイルを読んでいるとしましょう。ストレージシステムによっては、処理する前にファイル全体を読み取る方が速い場合があります。これは、ファイルを複数回検索しようとした場合に特に当てはまりますzipgrep。ファイルがメモリに完全に保存されていない場合は、ディスクがスキャンを実行するのを待ちます。

find ... | parallel -j1 'cat {} >/dev/null; echo {}' | parallel zipgrep "my search string"

上記のcatコードは、一度に1つのファイルをメモリキャッシュに配置し、zipgrepCPUごとに一度に1つずつファイルを実行し、メモリキャッシュから読み込みます。

私はRAIDシステムを使用していましたが、10個のファイルを並列に読み取るのが一度に1個のファイルを読み取るか、30個のファイルを並列に読み取るよりも6倍速くなりました。このRAIDシステムで上記のタスクを実行する必要-j1がある場合-j10

代わりにGNU Parallelを使用すると、xargs出力ミックスから自分自身を保護できます(参照http://www.gnu.org/software/parallel/man.html#DIFFERENCES-BETWEEN-xargs-AND-GNU-Parallel)。

関連情報