この回答は、スクリプトがなぜ遅いのかと理由を確認する方法を教えてくれます。 PerlプロファイラDevel::NYTProfを使用します。これはCPANからインストールする必要があります。
これを行う前に、this talkをプロファイリングについての著者に見てください。これはまれにしか行われないことを知っておくことが重要です。あなたはそのようなケースです。
まず、the JSON API of baconipsum.comを使用して、次のコマンドを使用してテスト入力を作成します。このファイルは70メガバイト程度で1万行を持つことになります
$ curl -s \
'https://baconipsum.com/api/?type=meat-and-filler&format=text&sentences=100' \
| perl -nE 'for $i (1 .. 10_000) { say for map lc, split /\./}' >log.txt
。それだけで十分です。
$ ls -lah
-rw-rw-r-- 1 simbabque simbabque 69M Nov 28 13:36 log.txt
$ tail log.txt
Nisi magna pig pastrami, in chicken elit meatball
Consequat laborum rump kevin beef ham hock proident tempor ex strip steak
Shankle kielbasa in nulla
Consectetur picanha pork belly, drumstick tail tempor alcatra pariatur eiusmod
Tongue tail meatloaf cupim ut do sed, cillum kevin id ex dolore t-bone
Ut cow nulla brisket ball tip ipsum ham strip steak culpa cillum
Doner chicken sint duis in, andouille labore eiusmod
Bacon tempor nostrud, short loin occaecat cow nulla ipsum strip steak pastrami corned beef turducken
Ball tip labore chicken pancetta cupim
Ham leberkas pastrami, exercitation id porchetta tri-tip beef voluptate shoulder ipsum meatloaf sunt ea.
次に、スクリプトを準備します。私は、3つの引数open
とレキシカルファイルハンドルのように、より現代的なPerlにするためにいくつかの変更を加えました。
$ cat patterns.pl
use strict;
use warnings;
use Data::Dumper;
my $InListOfPatterns = '[email protected]@@[email protected]@@steak';
my $InStartLineNumber = 2;
my @pat_array = split('@@@', $InListOfPatterns);
my $num_pat = @pat_array;
my @match_count;
for (my $i = 0; $i < $num_pat; $i = $i + 1) {
$match_count[$i] = 0;
}
open my $fh,'<','log.txt' or die "can not open file :$!";
while (<$fh>) {
chomp;
if ($. > $InStartLineNumber) {
for (my $j = 0; $j < $num_pat; $j = $j + 1) {
if ($_ =~ m/\Q$pat_array[$j]\E/) {
$match_count[$j] = ($match_count[$j] + 1);
}
}
}
}
print Dumper \@match_count;
これは私のマシンでは6秒のようなもので、最後にパターンごとの一致数が表示されます。
ここでは、Devel::NYTProfでこれをどのようにプロファイルできるかを見ていきましょう。このコマンドを実行するだけです。 -d
フラグはPerlにデバッガインタフェースを使用するように指示し、:NYTProf
はDevel::NYTProf
デバッガを使用するように指示します。
$ perl -d:NYTProf patterns.pl
$VAR1 = [
20000,
300000,
90000
];
あなたのディレクトリにnytprof.out
というファイルがあります。
$ nytprofhtml --no-flame --open
Reading nytprof.out
Processing nytprof.out data
Writing line reports to nytprof directory
100% ...
それは、既存のブラウザでブラウザウィンドウまたは新しいタブを開き、あなたにこのような何かが表示されます:
我々はのラインレポートに行きたいですpatterns.pl。赤い線は、NYTProfが非常に遅いと判断するものです。
最も明白なのは、行17のchomp
です。これは、破棄された行でも呼び出されます。もちろん、私たちの例では1行をスキップしただけでしたが、あなたの場合はそれ以上であるかもしれません。 if
の後にchomp
を移動します。
は、我々はまた、最も重要な時間はif
に費やされていることがわかります。 chorboa says in his answer on your cross-posted Perlmonks questionという名前のキャプチャグループでは、単一のパターンを使用できます。私はこれを2つのステップで実証するので、なぜ彼は彼がしたことをしたのか分かります。
use strict;
use warnings;
use Data::Dumper;
my $InListOfPatterns = '[email protected]@@[email protected]@@steak';
my $InStartLineNumber = 2;
my @pat_array = split('@@@', $InListOfPatterns);
open my $fh, '<', 'log.txt' or die "can not open file :$!";
my %matched;
while (<$fh>) {
if ($. > $InStartLineNumber) {
chomp;
# these two are inside the loop, which is bad
my $i;
my $regex = join '|', map +($i++, "(?<m$i>$_)")[1], map quotemeta, @pat_array;
$matched{ (grep defined $-{$_}[0], keys %-)[0] }++ if /$regex/;
}
}
print Dumper \%matched;
プロファイラをもう一度実行し、結果を確認してください。ループの内部ではより複雑な操作を行うため、処理に時間がかかります。それは良くないね。
それはほぼ100万行のすべての単一ため改めて同一のパターンをコンパイルし、ライン18上のそのループの内側に約2秒を過ごしました。
だから、あなたは明らかにループの外に移動したいと思っています。例えば、チョロバが彼の投稿に持っていたように。
use strict;
use warnings;
use Data::Dumper;
my $InListOfPatterns = '[email protected]@@[email protected]@@steak';
my $InStartLineNumber = 2;
my @pat_array = split('@@@', $InListOfPatterns);
my $i;
my $regex = join '|', map +($i++, "(?<m$i>$_)")[1], map quotemeta, @pat_array;
open my $fh, '<', 'log.txt' or die "can not open file :$!";
my %matched;
while (<$fh>) {
if ($. > $InStartLineNumber) {
chomp;
$matched{ (grep defined $-{$_}[0], keys %-)[0] }++ if /$regex/;
}
}
print Dumper \%matched;
これをプロファイラで再実行すると、これが報告されます。
パターン生成のための呼び出しは、今一度だけ呼ばれます。それははるかに高速です。
残念ながら、このファイルサイズでは、全体的な勝利はあまり高くありません。元のコードのfor
ループでは12.4秒+1.1秒でしたが、今は12.2です。それらの1.3秒は、わずか100万行ではあまり意味がありません。しかし、ファイルにはさらに多くの行がある可能性があります。特に可能なパターンを追加すると、合計で少し速くなります。
パターンを5つに増やすと、新しい実装では23.6秒、元の実装では1.78秒+ 23.6秒になります。それは1.78sの違いです。
3つではなくループ内で1つの一致が得られるというメリットは非常にはっきりしていますが、どのパターンが一致しているかを把握するキャプチャグループは価格が高く、 。
代わりにこれをソリューションin Sobrique's answerと比較すると、元の1.78s + 23.6sに対して3.69sとなります。この差は現在ほぼ1桁であり、非常に重要です。序数でパターンを取得するには、ループ外に1行または2行の追加コードを書く必要がありますが、これはごくわずかです。
すべての測定値はマシンによって大きく異なり、同時に実行される他のプロセスの影響を受けることに注意してください。あなたのコンピュータ上では全く異なるかもしれません。ベンチマークは難しく、しばしばあまり正確ではありません。
あなたの質問は[codereview.se]でうまく収まるでしょう。あなたはすでに作業コードを持っていて、改善したいと思っています。いずれにせよ、[編集]し、テストするログの完全な行をいくつか含めて、パターンのリストを表示してください。その情報を開示したくない場合は、同じ問題を示すサンプルデータを含む新しいプログラムを作成し、それを含めます。これは[mcve]と呼ばれます。 – simbabque
_新しいコード/アイデアを手伝ってくれたとき._「いつ」は – ssr1012
xpost w/answers http://www.perlmonks.org/?node_id=1204414 – toolic