ま、perl で「固定長文字列を取り出すなら substr() が速い」 という、ごく自然な話です。。
ある ","(カンマ) 区切りのデータが1行1件で何行かある。 そのデータを抜き出してあれこれするというコードを書く必要があって 適当に split /PATTERN/,EXPR,LIMIT していたわけです。 ところが、その「何件か」が10万件になったときに、 さすがにこれは時間かかりすぎとういう気がして 「split より 正規表現使ったほうが 速いよね」という声を思い出して書き換えた。
$ diff 00split.pl 01regex.pl
7,8c7
< s/\x0d\x0a$//;
< my($id, $pass) = split(/,/, $_, 2, NULL);
---
> my($id, $pass) = /^(.{8}),(.{8})/;
データのお尻にはCRLFがついているので split の場合は削除する必要がある。regex の場合にお尻のパターンマッチも加えると遅くなったので 「頭から8文字。カンマをはさんでもう8文字」という形にしている。
けど、あんまり速くならない。2%くらい。 で、「頭から8文字。カンマをはさんでもう8文字」なら substr でいいじゃん。 ってことを思い出してやってみた。
$ diff 00split.pl 02substr.pl 7,8c7 < s/\x0d\x0a$//; < my($id, $pass) = split(/,/, $_, 2, NULL); --- > my ($id, $pass) = (substr($_, 0, 8), substr($_, 9, 8));
こうするとsplitの時よりも 20%ほど速くなりました。
$ perl bench.pl Benchmark: timing 5 iterations of 00split, 01regex, 02substr... 00split: 6 wallclock secs ( 0.00 usr 0.01 sys + 5.83 cusr 0.04 csys = 5.88 CPU) @ 500.00/s (n=5) 01regex: 6 wallclock secs ( 0.00 usr 0.01 sys + 5.75 cusr 0.06 csys = 5.82 CPU) @ 500.00/s (n=5) 02substr: 5 wallclock secs ( 0.00 usr 0.01 sys + 4.88 cusr 0.07 csys = 4.96 CPU) @ 500.00/s (n=5)
ベンチマークに使用したスクリプトは以下。ベンチマーク計測用にデータは1000件に変えてあります。
use Benchmark;
timethese(5, {
'00split' => sub{system("perl 00split.pl 1000.csv > /dev/null")},
'01regex' => sub{system("perl 01regex.pl 1000.csv > /dev/null")},
'02substr' => sub{system("perl 02substr.pl 1000.csv > /dev/null")},
});
それぞれのコードのdiff はこんな感じ。
$ diff3 0*.pl
====
1:7,8c
s/\x0d\x0a$//;
my($id, $pass) = split(/,/, $_, 2, NULL);
2:7c
my($id, $pass) = /^(.{8}),(.{8})/;
3:7c
my ($id, $pass) = (substr($_, 0, 8), substr($_, 9, 8));

Comments