[Perl]UTF8-flagged strings affects regexps with the "i" modifier
HTML::FillInForm::Liteの使いどころという記事で,HTML::FillInForm::Liteが遅いということが取り上げられていた。
試しに記事内のベンチマークを行ったところ,確かに遅い。
# HTML::FillInForm 1.06 Benchmark: HTML::FillInForm vs. HTML::FillInForm::Lite Rate fillinall_lite fillinall fillinpart_lite fillinpart fillinall_lite 570/s -- -52% -76% -78% fillinall 1199/s 110% -- -49% -54% fillinpart_lite 2349/s 312% 96% -- -9% fillinpart 2595/s 355% 116% 10% --
ところで,このベンチマークの対象となっている文字列はutf8-flaggedだ。Perlではutf8フラグ付きの文字列に対するuc/lc/"i"正規表現修飾子は非常に遅いのだが,H::F::Liteでは/iを使っているので,試しに/iを使わないようにしてみると,速度が改善した*1。具体的にはm/
# HTML::FillInForm::Lite 1.07 fillinall 1185/s -- -53% -54% -60% fillinall_lite 2535/s 114% -- -2% -15% fillinpart 2595/s 119% 2% -- -13% fillinpart_lite 2983/s 152% 18% 15% --
従来の結果の通り,H::F::Liteのほうが常に高速になった。utf8 flags + /iが遅いだろうとは思っていたが,ここまで影響が大きいとは思わなかった。
そこで,純粋に/iの速度のみを比較するベンチマークをとってみた。
スクリプト:
#!perl -w use strict; use Benchmark qw(:all); # pronounced as 'zdrastvuiche' my $s = qq{Здравствуйте\n}; my $w = $s; utf8::decode($w); cmpthese -1 => { 'bytes without /i' => sub{ $s =~ /$s/o for 1 .. 100; }, 'bytes with /i' => sub{ $s =~ /$s/io for 1 .. 100; }, }; cmpthese -1 => { 'utf8 without /i' => sub{ $w =~ /$w/o for 1 .. 100; }, 'utf8 with /i' => sub{ $w =~ /$w/io for 1 .. 100; }, }; __END__
結果:
Rate bytes with /i bytes without /i bytes with /i 21622/s -- -54% bytes without /i 46897/s 117% -- Rate utf8 with /i utf8 without /i utf8 with /i 302/s -- -99% utf8 without /i 43530/s 14315% --
この結果によれば,bytesでも/iによってパフォーマンスは50%程度落ちる。ところがutf8-flaggedの/iによるパフォーマンスの低下は尋常ではなく,/iによって1/140程度にまで性能が悪化する。2倍や10倍というレベルではない。H::F::Liteの性能が悪くなるわけだ。
もちろん,utf8-flaggedでなければ難しい処理もあるため,むやみにutf8-flaggedを避ける必要はない。たとえば今回使った/iのためのベンチマークのように,非ASCII文字でも簡単にuc/lc/iできるが,これを手動で行うのは難しい。
なお,uc/lc/iがこんなにも遅いのは,一文字毎にハッシュマップを引くからだ。また,これらの文字データは外部ファイルに保存されており,必要に応じてロードされる。
参考:
#!perl -w use utf8; # pronounced as 'zdrastvuiche' my $s = qq{Здравствуйте\n}; binmode STDOUT, ':utf8'; print 'uc: ', uc $s; print 'lc: ', lc $s; print join(" ", sort keys %INC), "\n"; __END__
結果:
uc: ЗДРАВСТВУЙТЕ lc: здравствуйте strict.pm unicore/Canonical.pl unicore/Exact.pl unicore/PVA.pl unicore/To/Lower.pl unicore/To/Upper.pl utf8.pm utf8_heavy.pl warnings.pm