substr, regex, split スピード対決 (出来レース)

| コメント(0)

ま、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

コメントする