私はsed
andに関連するいくつかのパフォーマンス問題を理解しようとしており、awk
次の実験を行いました。
$ seq 100000 > test
$ yes 'NR==100001{print}' | head -n 5000 > test.awk
$ yes '100001{p;b}' | head -n 5000 > test.sed
$ time sed -nf test.sed test
real 0m3.436s
user 0m3.428s
sys 0m0.004s
$ time awk -F@ -f test.awk test
real 0m11.615s
user 0m11.582s
sys 0m0.007s
$ sed --version
sed (GNU sed) 4.5
$ awk --version
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 3.1.6-p2, GNU MP 6.1.2)
test.sed
ここでは、テストファイルには100,000行しか含まれていないため、およびすべてのコマンドはtest.awk
機能しません。どちらのプログラムも単に行番号をアドレス(in sed
)またはNR
(in awk
)に一致させてコマンドを実行する必要はないと判断しますが、それでも時間コストに大きな違いがあります。なぜですか?別のバージョンをインストールsed
し、awk
このテストで別の結果を得た人はいますか?
編集する:結果mawk
(@mosvyが提案したように)original-awk
(@GregA.Woodsが提案したDebianベースのシステムの「one true awk」名)perl
は次のとおりです。
$ time mawk -F@ -f test.awk test
real 0m5.934s
user 0m5.919s
sys 0m0.004s
$ time original-awk -F@ -f test.awk test
real 0m8.132s
user 0m8.128s
sys 0m0.004s
$ yes 'print if $.==100001;' | head -n 5000 > test.pl
$ time perl -n test.pl test
real 0m33.245s
user 0m33.110s
sys 0m0.019s
$ mawk -W version
mawk 1.3.4 20171017
$ perl --version
This is perl 5, version 28, subversion 1 (v5.28.1) built for x86_64-linux-thread-multi
と の場合に-F@
は を置き換えても-F ''
目立つ変化はありません。空白はサポートされていません。gawk
mawk
original-awk
FS
編集2
@mosvyのテストでは、21秒sed
と11秒という異なる結果が出ましたmawk
。詳細については、以下の説明を参照してください。
答え1
awk
より広範な機能セットsed
とより柔軟な構文を持っています。だから、スクリプトを解析して実行するのに時間がかかるのは無理ではありません。
例コマンド(中括弧内の部分)は実行されないため、時間に敏感な部分はテスト式でなければなりません。
アッ
awk
まず、例のテストを見てください。
NR==100001
gprof
そして(GNU awk 4.0.1)の効果をチェックしてください:
累積自己合計% 時 秒 秒 通話 s/通話 s/通話名 55.89 19.73 19.73 1 19.73 35.04 説明 8.90 22.87 3.14 500000000 0.00 0.00 cmp_スカラー 8.64 25.92 3.05 1000305023 0.00 0.00 free_wstr 8.61 28.96 3.04 500 105 014 0.00 0.00 mk_number 6.09 31.11 2.15 500000001 0.00 0.00 cmp_nodes 4.18 32.59 1.48 500200013 0.00 0.00 参照しない 3.68 33.89 1.30 500000000 0.00 0.00評価条件 2.21 34.67 0.78 500000000 0.00 0.00 更新_NR
解析スクリプトで生成されたオペコードを実行する最上位ループである「解釈」には約50%の時間がかかります。
テストを実行するたびに (たとえば、5000 スクリプト行 * 100000 入力行など)、次の操作を行うawk
必要があります。
- 組み込み変数 "NR"(
update_NR
) を取得します。 - 文字列 "100001"(
mk_number
) を変換します。 - (
cmp_nodes
、、、cmp_scalar
)eval_condition
を比較してみてください。 - 比較に必要な一時オブジェクトをすべて削除します(
free_wstr
、、unref
)。
他のawk
実装では、呼び出しフローはまったく同じではありませんが、変数を検索して自動的に変換して比較する必要があります。
sed
対照的に、sed
「テスト」ははるかに制限されています。単一のアドレス、アドレス範囲、または何もできません(コマンドが行の最初のエントリである場合)、最初のアドレスからsed
始めることができます。特徴住所であれ命令であれ。例では
100001
...単一の数値アドレス。設定ファイル(GNU sed 4.2.2)は次のとおりです。
累積自己合計% 時 秒 秒 通話 s/通話 s/通話名 52.01 2.98 2.98 100000 0.00 0.00プログラムの実行 44.16 5.51 2.53 1000000000 0.00 0.00 一致 address_p 3.84 5.73 0.22 一致するアドレス [...] 0.00 5.73 0.00 5000 0.00 0.00 整数
繰り返しますが、最上位レベルで50%程度の時間がかかりますexecute_program
。この場合、各入力行に対して一度呼び出され、解析されたコマンドを繰り返します。ループはアドレス解決で始まりますが、例ではこれはすべてではありません(下記参照)。
入力スクリプトの行番号はコンパイル時に解析されます(in_integer
)。これは、入力の各アドレス番号に対して一度だけ行うことができます。 5000回でフルランタイムに大きく寄与しません。
つまり、アドレス解決は、match_address_p
すでに使用可能な整数(構造とポインタを介して)のみを比較することを意味します。
さらなるsed
改善
構成ファイルは、match_address_p
2 * 5000 * 100000回呼び出されるとマークされます。二重各スクリプト行 * 入力行。これは、GNUが後でsed
「ブロック開始」コマンドを処理するためです。
100001{...}
ブロックの終わりまでの負の分岐で
100001!b end; ... :end
住所が一致成功各入力ラインでブロックの終わり(}
)に分岐します。ブロックの最後に関連するアドレスがないので、これは別の成功した一致です。それがなぜそれほど時間がかかるのかを説明しますexecute_program
。
したがって、sed
使用しない内容を省略し、;b
結果的に不要な内容が発生する場合{...}
。100001p
累積自己合計% 時 秒 秒 通話 s/通話 s/通話名 71.43 1.40 1.40 500000000 0.00 0.00 一致するアドレス 24.49 1.88 0.48 100000 0.00 0.00プログラムの実行 4.08 1.96 0.08 一致するアドレス
これにより、呼び出しの数が半分になり、match_address_p
所要時間も大幅に短縮されますexecute_program
(アドレスの一致が成功しないため)。
答え2
実際、上記のスクリプトはawkには適していません。
フィールドの内容を使用しなくても状況に応じてGAWKマニュアル各レコードを読み込むたびに、次のステップは必然的に実行されます。
- すべてのFS発生を調べます。
- フィールド分割
- NF変数の更新
この情報を使用しない場合は破棄されます。
レコードにフィールド区切り文字が表示されない場合は、awkはまだテキストを$ 0(あなたの場合は$ 1)に割り当て、NFをインポートしたフィールドの実際の数(上記の例では1)に設定する必要があります。