5

bash での sed の効率について質問があります。パイプライン化された一連の sed ステートメントがあります。

var1="Some string of text"

var2=$(echo "$var1" | sed 's/pattern1/replacement1/g' | sed 's/pattern2/replacement2/g' | sed 's/pattern3/replacement3/g' | sed 's/pattern4/replacement4' | sed 's/pattern5/replacement5/g')

入力が以前の sed パイプから編集された出力に依存しないと仮定すると、代わりに式ステートメントを使用して上記をスクリプト化する方がよいでしょうか? 例えば:

var2=$(echo "$var1" | sed -e's/pattern1/replacement1/g' -e's/pattern2/replacement2/g' -e's/pattern3/replacement3/g' -e's/pattern4/replacement4/g' -e's/pattern5/replacement5/g')

ここで得られる効率はありますか?

4

5 に答える 5

9

簡潔な答え

複数の式を使用すると、複数のパイプラインを使用するよりも高速になります。これは、パイプラインの作成と sed プロセスのフォークに追加のオーバーヘッドがあるためです。ただし、実際には問題になるほどの違いはめったにありません。

ベンチマーク

複数の式を使用することは、複数のパイプラインよりも高速ですが、平均的なユース ケースではおそらく十分ではありません。あなたの例を使用すると、実行速度の平均差はわずか 2,000 分の 1 秒であり、興奮するほどではありません。

# Average run with multiple pipelines.
$ time {
    echo "$var1" | 
    sed 's/pattern1/replacement1/g' |
    sed 's/pattern2/replacement2/g' |
    sed 's/pattern3/replacement3/g' |
    sed 's/pattern4/replacement4/g' |
    sed 's/pattern5/replacement5/g'
}
Some string of text

real        0m0.007s
user        0m0.000s
sys         0m0.004s

# Average run with multiple expressions.
$ time {
    echo "$var1" | sed \
    -e 's/pattern1/replacement1/g' \
    -e 's/pattern2/replacement2/g' \
    -e 's/pattern3/replacement3/g' \
    -e 's/pattern4/replacement4/g' \
    -e 's/pattern5/replacement5/g'
}
Some string of text

real        0m0.005s
user        0m0.000s
sys         0m0.000s

確かに、これは大きな入力ファイル、数千の入力ファイル、または数万回の反復を伴うループでの実行に対するテストではありません。それでも、違いはほとんどの一般的な状況に関係がないほど小さいと言っても過言ではありません。

珍しい状況は別の話です。このような場合、ベンチマークは、パイプをインライン式に置き換えることがそのユースケースにとって価値のある最適化であるかどうかを判断するのに役立ちます。

于 2012-07-25T01:30:54.593 に答える
4

sed のオーバーヘッドのほとんどは正規表現を処理する傾向がありますが、各例で同じ数の正規表現を処理しています。

オペレーティング システムは、パイプの各要素に対して std と stdout を構築する必要があることを考慮してください。Sed はシステム内のメモリも必要とし、OS は sed のインスタンスごとにそのメモリを割り当てる必要があります (それが 1 つであろうと 4 つであろうと)。

これが私の評価です:

$ jot -r 1000000 1 10000 | time sed 's/1/_/g' | time sed 's/2/_/g' | time sed 's/3/_/g' | time sed 's/4/_/g' >/dev/null 
        2.38 real         0.84 user         0.01 sys
        2.38 real         0.84 user         0.01 sys
        2.39 real         0.85 user         0.01 sys
        2.39 real         0.85 user         0.01 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.71 real         2.57 user         0.02 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.71 real         2.56 user         0.02 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.71 real         2.57 user         0.02 sys
$ jot -r 1000000 1 10000 | time sed 's/1/_/g;s/2/_/g;s/3/_/g;s/4/_/g' >/dev/null
        2.74 real         2.57 user         0.02 sys
$ dc
.84 2* .85 2* + p
3.38
$ 

また、3.38 > 2.57 以降では、sed のインスタンスを 1 つだけ使用すると、時間がかかりません。

于 2012-07-25T01:12:17.750 に答える
2

はい。毎回sedを新たに開始するオーバーヘッドを回避できます。

于 2012-07-25T01:04:42.620 に答える
0

ghoti's answer に記載されているように、あなたの例には、どちらの場合でも同じ数の正規表現があります (sed一連の-e式に対する個別の呼び出し) が、OS のオーバーヘッドには、パイプラインとプロセスのセットアップ、および の各インスタンスのメモリ割り当てが含まれますsed。ほんの一握りの呼び出しの場合、OS のオーバーヘッドは気にする必要はありませんが、数が数千以上の場合は気にする必要があります。

とにかく、コンピューターの効率はさておき、プログラマーの効率はしばしばより重要な関心事です。これまでに示した両方の方法は、不器用で入力が遅い. (少なくとも GNU sed では)sed多数の個別の文字列の代わりに、セミコロンで区切られたコマンド リストを使用する方が簡単-eです。以下に例を示します。

$ var1="Some p1 string p2 of p3 text p4 etc"
$ var2=$(echo "$var1" | sed 's/p1/a1/g; s/p2/b2/g; s/p3/c3/g; s/p4/d4/; s/p5/e5/g')
$ echo $var2
Some a1 string b2 of c3 text d4 etc

残念ながら、sed-command-separator としてのセミコロンがsedドキュメントに記載されていません。これが GNU sed 以外のバージョンで利用できるかどうかもわかりません。

于 2012-09-16T15:27:54.937 に答える
0

おそらく効率を測定して、違いを判断できます。おそらくtimeコマンドを使用しています。ただし、経験的には -e の方が効率的です。

于 2012-07-25T01:14:05.970 に答える