Bashスクリプトを最適化するための一般的なガイドラインがあるかどうか疑問に思います。
たとえば、便利コマンドラインではなくループを作成しますが、これもより速い処理システムのため?例:
for i in a b c; do echo $i; done echo a echo b echo c
時々、人々は同じ問題に対して異なる解決策を思い出します。たとえば、
sed
、 、ともにcut
文字列から数字を削除できます。次のコードを使用すると、数値が低いほど速度が速くなると言えるかどうか疑問に思います。awk
echo
同じコマンド
STRING=abc.def echo ${STRING} | sed 's/.def//g' echo ${STRING} | sed '$s/....$//'
次のような他のコマンド
STRING=abc.def echo ${STRING} | cut -d . -f 1 echo ${STRING} | sed 's/.def//g'
答え1
最適化の最初のルールは次のとおりです。最適化されていません。まずテストしてみてください。テスト結果プログラムが遅すぎると思われる場合は、可能な最適化方法を見つけてください。
確かに知ることができる唯一の方法は、ユースケースをベンチマークすることです。いくつかの一般的なルールがありますが、一般的なアプリケーションの一般的なデータ量にのみ適用されます。
状況によっては、一部の一般規則が適用されない場合があります。
- シェル内部処理の場合、ATT ksh が最も高速です。文字列操作が多い場合は、ATT kshを使用してください。 Dashが2位で、bash、pdksh、zshが遅れています。
- 一度に短い操作を実行するためにシェルを頻繁に呼び出す必要がある場合、開始時間が短いためダッシュが勝ちます。
- 外部プロセスを開始するには時間がかかるため、複雑な部分を含むパイプラインを持つ方がループのパイプラインよりも高速です。
echo $foo
echo "$foo"
二重引用符がない場合は、$foo
単語に分割して各単語をファイル名のワイルドカードパターンとして解釈するため、それより遅くなります。さらに、これらの分割とワイルドカードの動作はほとんど必要ありません。したがって、変数置換とコマンド置換には常に二重引用符を入れる必要が"$foo"
あることに注意してください。"$(foo)"
- 特殊目的ツールは、しばしば汎用ツールよりも優れています。たとえば、
cut
または同じツールをhead
使用してシミュレートできますが、sed
速度sed
が遅くなったり遅くなったりしますawk
。シェル文字列の処理は遅いですが、短い文字列の場合は、外部プログラムを呼び出すよりもパフォーマンスがはるかに優れています。 - Perl、Python、Rubyなどの高度な言語を使用すると、より高速なアルゴリズムを書くことができることがよくありますが、開始時間がはるかに長いため、大量のデータを扱う場合にのみ実行する価値があります。
- 少なくともLinuxでは、パイプは一時ファイルよりも速い傾向があります。
- シェルスクリプトのほとんどの使用はI / O集約的なプロセスを中心に行われているため、CPUの消費は重要ではありません。
パフォーマンスの問題はシェルスクリプトではほとんど考慮されません。上記のリストは純粋に例示的なものです。ほとんどの場合、違いはほんの数パーセントしかないので、「遅い」方法を使用することをお勧めします。
通常、シェルスクリプトの目的は、タスクをすばやく完了することです。スクリプトの作成に余分な時間を費やすことを正当化するには、最適化によってかなりの利点を得る必要があります。
答え2
シェルは受信したコードを再構成せずに1行ずつ解釈するだけです(コマンドソルバーでは他に何も意味がありません)。シェルが費やす時間のほとんどは、呼び出すプログラムを解析して起動するのにかかります。
単純な操作(質問の末尾にある例の文字列を変更するなど)の場合、プログラムの読み込みにかかる時間のために小さな速度の違いが消えない場合は驚きます。
ストーリーのレッスンは、本当に速いスピードが必要な場合は、PerlやPythonなどの(半)コンパイル言語を使用する方が良いことです。 Perl や Python は、始めるより速く、直接言及された多くのタスクを書くことができます。外部プログラムを呼び出す必要はなく、ほとんどのタスクを実行するために外部プログラムを呼び出すか、最適化されたC(または他の)モジュールを呼び出すかを選択できます。これがFedoraで「システム管理砂糖」(本質的にGUI)がPythonで書かれた理由です。素晴らしいGUIを追加するのに多くの労力は必要ありません。この種のアプリケーションには十分速く、システムコールに直接アクセスできます。 。速度が十分に速くない場合は、C ++またはCを選択してください。
しかし、欲しくないできない場合はそこに行きます。証明するパフォーマンスの向上は、柔軟性と開発時間の損失と同じくらい価値があります。シェルスクリプトはよく読みますが、Ultrixのインストールに使用されたスクリプトの一部を復号化しようとすると、体が震えます。あきらめ、あまりにも多くの「シェルスクリプトの最適化」を適用しました。
答え3
ここでは、上記のワイルドカードの例を拡張して、シェルスクリプトインタプリタのいくつかのパフォーマンス特性を説明します。この例では、30,000個のファイルごとにプロセスが生成され、
bash
インタプリタを比較すると、ダッシュフォークがほぼ高速で処理されることがわかります。dash
wc
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システムの既存のロジックで利用可能な豊富なユーティリティセットに接続することを目的とする必要があります。
で盗まれた一般的なシェルスクリプトエラー著者: Padraig Brady。
答え4
私のクイックアクション:
サブシェルを使用しないでください。
- (サブシェルなし)は(サブシェル)の
{ }
代わりに頻繁に/時々使用できることに注意してください。( )
- (サブシェルなし)は(サブシェル)の
シェル組み込み機能を使用すると、外部コマンドが簡単になります。
date()
/usr/bin/date
たとえば、代わりにシェル関数を呼び出して日付を取得するにはprintf
シェル変数を使用すると、シェル組み込みコマンドやコマンド呼び出しが容易になります。
$SECONDS
たとえば、現在の時刻を取得するには、呼び出しを呼び出す代わりにクエリを使用できる場合は、date()
前者が高速です。
文字列操作では、変数拡張を使用して外部コマンド呼び出し(など)などの
${...}
代替をサポートします。cut
tr
テキストファイルで作業するときは、可能であれば変数を1行ずつ読み取ることを避け、代わりにファイル全体に対して、などの外部コマンドを使用し
sed
てくださいtr
。cut
- データフィルタリング(情報の縮小)を含むデータ処理のためにテキストファイルを変数として読み取る必要がある場合は、外部コマンドを使用してファイル全体をフィルタリングし、フィルタリングされた(縮小された)データを変数として読みます。
正規表現を使用している場合は、バックトラッキングを防ぐために慎重に作成してください。
- 誤って書かれた正規表現のパフォーマンスは、時には同等の正規表現よりも100倍以上悪いことがあります。