Bashスクリプト。処理速度の最適化


10

Bashスクリプトを最適化するための一般的なガイドラインがあるかどうか疑問に思っていました。

  • たとえば、コマンド行よりもループを記述する方が便利ですが、システムの処理高速ですか?例:

    for i in a b c; do echo $i; done
    
    echo a
    echo b
    echo c
  • 時々人々は同じ問題に対して異なる解決策を提示します。たとえば、sedcutawk、およびechoすべての文字列から数字を削除することができます。数字のコードが少ないほど、使用すると速くなると言えるのではないかと思いました。

    1. 同じコマンド、例えば

      STRING=abc.def
      echo ${STRING} | sed 's/.def//g'
      echo ${STRING} | sed '$s/....$//'
    2. 異なるコマンド、例えば

      STRING=abc.def
      echo ${STRING} | cut -d . -f 1
      echo ${STRING} | sed 's/.def//g'

2
ほとんどの場合、速度よりも可読性を優先します。
Bernhard

1
a)はい、forループの使用にはオーバーヘッドがありますが、それは問題ではありません。b)外部コマンドを最小限に抑え、bash内のものを置き換えるだけです。シェルスクリプトを最適化する必要がある場合は、何か問題が発生しているため、より優れたプロファイラーサポートを備えた言語の使用を検討する必要があります。
Ulrich Dangel 2013年

正規表現は一般に、他のどの選択肢よりも効率が悪いので、何もない方法(カットなど)を見つける方法がある場合は、それを使用します。もちろん、あなたの読解力が正規表現に制限されている場合、それ可読性に影響を与える可能あります;)
goldilocks 2013年

回答:


8

シェルは、渡されたコードの再編成を行わず、1行ずつ解釈されるだけです(コマンドインタープリターでは他に何も意味がありません)。シェルが費やす時間の多くは、呼び出されるプログラムの字句解析/解析/起動に費やされます。

簡単な操作(質問の最後にある例の文字列を変更する操作など)の場合、プログラムをロードする時間が非常に小さな速度の違いに影響されない場合、私は驚かれることでしょう。

この話の教訓は、本当にもっと速度が必要な場合は、PerlやPythonなどの(準)コンパイル済み言語を使用するほうがよいということです。また、外部プログラムを呼び出す必要はありません。外部プログラムを呼び出すか、最適化されたC(またはその他の)モジュールを呼び出して、ほとんどの作業を行うことができます。これが、Fedoraで「システム管理シュガー」(基本的にGUI)がPythonで書かれている理由です。それほど多くの労力をかけずに、そのようなアプリケーションに十分な速度で素晴らしいGUIを追加でき、システムコールに直接アクセスできます。それでも十分な速度でない場合は、C ++またはCを入手してください。

ただし、パフォーマンスの向上が柔軟性と開発時間の損失に見合う価値があることを証明できない限り、そこに行ってはいけません。シェルスクリプトは読むにはそれほど悪くありませんが、Ultrixをインストールするために使用されたいくつかのスクリプトを思い出したとき、私はかつて解読しようとしました。私はあきらめました、あまりにも多くの「シェルスクリプト最適化」が適用されていました。


1
+1 しかし、多くの人は、Pythonやperlとシェルのどちらかを使用することで、損失ではなく、柔軟性と開発時間の増加が見込まれると主張します。シェルスクリプトは、必要な場合にのみ使用するか、シェル固有のコマンドを大量に使用している場合に使用します。
goldilocks 2013年

22

最適化の最初のルールは、「最適化しない」です。最初にテストします。テストでプログラムが遅すぎることが判明した場合は、最適化の可能性を探します。

確実にする唯一の方法は、ユースケースのベンチマークを行うことです。いくつかの一般的なルールがありますが、それらは一般的なアプリケーションの一般的なデータ量にのみ適用されます。

特定の状況で当てはまる場合と当てはまらない場合があるいくつかの一般的な規則:

  • シェルの内部処理では、ATT kshが最も高速です。多くの文字列操作を行う場合は、ATT kshを使用してください。ダッシュは2番目です。bash、pdksh、zshは遅れています。
  • 毎回非常に短いタスクを実行するために頻繁にシェルを呼び出す必要がある場合は、起動時間が短いため、ダッシュが適しています。
  • 外部プロセスの開始には時間がかかるため、ループ内のパイプラインよりも複雑な部分を持つ1つのパイプラインを使用する方が高速です。
  • echo $fooecho "$foo"二重引用符がないため、よりも遅くなります。二重引用符がないため、$foo単語に分割され、各単語がファイル名のワイルドカードパターンとして解釈されます。さらに重要なことは、そのスプリッティングおよびグロビング動作がめったに望まれないことです。したがって、変数の置換とコマンドの置換を常に二重引用符で囲むことを忘れない"$foo"でください"$(foo)"
  • 専用ツールは、汎用ツールに勝る傾向があります。たとえば、cutまたはのようなツールheadはでエミュレートできますがsedsed遅くなり、awkさらに遅くなります。シェル文字列の処理は低速ですが、短い文字列の場合、外部プログラムの呼び出しよりもはるかに高速です。
  • 多くの場合、Perl、Python、Rubyなどのより高度な言語では、より高速なアルゴリズムを記述できますが、起動時間が非常に長いため、大量のデータのパフォーマンスにのみ価値があります。
  • 少なくともLinuxでは、パイプは一時ファイルよりも高速になる傾向があります。
  • シェルスクリプトのほとんどの使用は、I / Oバウンドプロセスを対象としているため、CPUの使用量は関係ありません。

シェルスクリプトでパフォーマンスが問題になることはまれです。上記のリストは純粋に目安です。ほとんどの場合、「遅い」方法を使用することはまったく問題ありません。その違いは、多くの場合、数分の1であるためです。

通常、シェルスクリプトのポイントは、何かを速く実行することです。スクリプトの作成に余分な時間を費やすことを正当化するには、最適化から多くを得る必要があります。


2
一方でpythonruby起動に間違いなく遅いです、私のシステムで、少なくとも、perlとして起動する迅速なようですbashksh。GNU awkは、特にutf-8ロケールでは、GNU sedより大幅に低速ですが、すべてのawkとすべてのsedには当てはまりません。ksh93> dash> pdksh> zsh> bashは、必ずしもそれほど明確ではありません。いくつかのシェルは他のものよりいくつかの点で優れています、そして勝者は常に同じではありません。
ステファンChazelas

2
「あなたから...多くを得るために持っている」場合:「あなたは」真、ユーザーベースを含んでいます。一般的なLinuxパッケージのシェルスクリプトを使用すると、多くの場合、ユーザーは急いでプログラマーが節約するよりも数桁も多くの時間を無駄に消費します。
agc 2017年

2

シェルスクリプトインタープリターのパフォーマンス特性を示すために、上記のグロビングの例をここで展開します。30,000のファイルごとにプロセスが生成されるこの例のbashdashインタープリターを比較すると、ダッシュがwcプロセスのフォークをほぼ2倍高速化できることがわかります。bash

bash-4.2$ time dash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.238s
user    0m0.309s
sys     0m0.815s


bash-4.2$ time bash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.422s
user    0m0.349s
sys     0m0.940s

wcプロセスを呼び出さずに基本ループ速度を比較すると、ダッシュのループがほぼ6倍速いことがわかります。

$ time bash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m1.715s
user    0m1.459s
sys     0m0.252s



$ time dash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m0.375s
user    0m0.169s
sys     0m0.203s

以前に示したように、どちらのシェルでもループはまだ比較的遅いため、スケーラビリティーのために、コンパイルされたプロセスで反復が実行されるように、より機能的な手法を使用する必要があります。

$ time find -type f -print0 | wc -l --files0-from=- | tail -n1
    30000 total
real    0m0.299s
user    0m0.072s
sys     0m0.221s

上記は断然最も効率的なソリューションであり、シェルスクリプトでできる限り少ないことを実行し、UNIXシステムで利用可能な豊富なユーティリティセットで利用可能な既存のロジックを接続するためだけに使用することを目的としています。

PádraigBradyによる一般的なシェルスクリプトの間違いから盗まれた。


1
一般的なルール:ファイル記述子の処理にもコストがかかるため、その数を減らします。for i in *; do wc -l "$i">/dev/null; doneより良い代わりにfor i in *; do wc -l "$i"; done>/dev/null
manatwork 2013年

@manatwork timecmdの出力もnullになります
Rahul Patil

@manatwork Good ... now呼び出しもせずにの出力も教えてください。出力をwc -l更新して確認してください
Rahul Patil

さて、以前の測定はより小さなディレクトリで行われました。今私は、30000個のファイルと1を作成し、テストを繰り返し:pastebin.com/pCV6QKp2を
manatwork

これらのベンチマークでは、各シェルの異なる開始時間を考慮することができません。各シェルから実行されるベンチマークの方が優れています。
agc 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.