私はに関連するいくつかのパフォーマンスの問題を理解しようとしていますsed
と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)
ここでは、テストファイルには100000行しか含まれていないため、test.sed
およびのすべてのコマンドtest.awk
は操作なしです。どちらのプログラムも、行番号をアドレス(in sed
)またはNR
(in awk
)と照合するだけで、コマンドを実行する必要がないと判断できますが、それでも時間コストには大きな違いがあります。なぜそうなのですか?このテストで異なる結果をもたらす、異なるバージョンsed
とawk
インストール済みの人はいますか?
編集:mawk
(original-awk
@ mosvyによって提案された)(@ GregA.Woodsによって提案されたdebianベースのシステムでの「onetrueawk 」の名前)の結果を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
られます。詳細については、以下のコメントを参照してください。
awk
より幅広い機能セットがありsed
、構文がより柔軟です。したがって、スクリプトの解析と実行の両方に時間がかかることは不合理ではありません。
サンプルコマンド(中括弧内の部分)は実行されないため、時間に敏感な部分をテスト式にする必要があります。
まず、awk
例のテストを見てください。
NR==100001
gprof
(GNU awk 4.0.1)でその効果を確認してください。
%累積自己自己合計 時間秒秒呼び出しs /呼び出しs /呼び出し名 55.8919.73 19.73 1 19.7335.04解釈 8.9022.87 3.14 500000000 0.00 0.00 cmp_scalar 8.64 25.92 3.05 1000305023 0.00 0.00 free_wstr 8.61 28.96 3.04 500105014 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 unref 3.68 33.89 1.30 500000000 0.00 0.00 eval_condition 2.21 34.67 0.78 500000000 0.00 0.00 update_NR
時間の約50%は、解析されたスクリプトから生成されたオペコードを実行するための最上位ループである「解釈」に費やされます。
テストが実行されるたびに(つまり、5000スクリプト行* 100000入力行)、次のことawk
を行う必要があります。
update_NR
)をフェッチします。mk_number
)を変換します。cmp_nodes
、cmp_scalar
、eval_condition
)。free_wstr
、unref
)他のawk
実装では、まったく同じ呼び出しフローはありませんが、変数を取得し、自動的に変換してから比較する必要があります。
比較すると、sed
では、「テスト」ははるかに制限されています。単一のアドレス、アドレス範囲、または何もない場合(コマンドが行の最初のものである場合)でありsed
、最初の文字からそれがアドレスであるかコマンドであるかを判別できます。例では、
100001
...単一の数値アドレス。プロファイル(GNU sed 4.2.2)は
%累積自己自己合計 時間秒秒呼び出しs / call s / call name 52.01 2.98 2.98 100000 0.00 0.00 execute_program 44.16 5.51 2.53 1000000000 0.00 0.00 match_address_p 3.84 5.73 0.22 match_an_address_p [...] 0.00 5.73 0.00 5000 0.00 0.00 in_integer
繰り返しますが、時間の約50%がトップレベルにありますexecute_program
。この場合、入力行ごとに1回呼び出され、解析されたコマンドをループします。ループはアドレスチェックから始まりますが、例ではそれだけではありません(後述)。
入力スクリプトの行番号は、コンパイル時に解析されました(in_integer
)。これは、入力のアドレス番号ごとに1回だけ実行する必要があります。5000回であり、全体の実行時間に大きな影響を与えることはありません。
つまり、アドレスチェックはmatch_address_p
、(構造体とポインタを介して)すでに使用可能な整数のみを比較します。
sed
改善プロファイルは、それmatch_address_p
が2 * 5000 * 100000回呼び出されることを示しています。script-line * input-lineごとに2回。これは、舞台裏で、GNUsed
が「スターティングブロック」コマンドを処理するためです。
100001{...}
ブロックの終わりへの否定されたブランチとして
100001!b end; ... :end
このアドレス一致はすべての入力行で成功し、ブロックの終わりに分岐します(}
)。そのブロックエンドにはアドレスが関連付けられていないため、別の成功した一致です。それが、に多くの時間が費やされている理由を説明していexecute_program
ます。
そのsed
ため、未使用のを省略し;b
、結果として不要なを省略して、。{...}
のみを残すと、式はさらに高速になり100001p
ます。
%累積自己自己合計 時間秒秒呼び出しs /呼び出しs /呼び出し名 71.431.40 1.40 500000000 0.00 0.00 match_address_p 24.49 1.88 0.48 100000 0.00 0.00 execute_program 4.08 1.96 0.08 match_an_address_p
これにより、match_address_p
呼び出しの数が半分になり、費やされる時間のほとんどが削減されますexecute_program
(アドレスの一致が成功しないため)。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加