webalizer用ログのデコード
今時webalizerを使っている人がどれだけいるのか知らないけど、仕事でデコードロジックを最適化する必要があったのでベンチマークを晒しておきます。
webalizerに食わせる前に、apacheログのURLをアンエスケープして、特定のエンコードに変換するというものです。
ちなみにぐぐって良く出てくるのはこれ。低レベル処理なのである程度早いかもしれないけど、どうみてもレガシー過ぎる。
s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; s/\\x([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; &Jcode::convert(\$_,"euc");
ベンチマークのコード。(@Pen M 1.73GHz、testlogは1000行)
use strict; use warnings; use URI::Escape::XS qw/uri_unescape/; use Encode qw//; use Jcode; use Benchmark qw/cmpthese/; my $file = shift || './testlog'; open my $fh, "<", $file or die $!; my $in_enc = Encode::find_encoding("utf8"); my $out_enc = Encode::find_encoding("eucjp"); cmpthese( -1, { Legacy => \&legacy, Simple => \&simple, Fast => \&fast, } ); sub legacy { seek $fh, 0, 0; while (<$fh>) { s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; s/\\x([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; &Jcode::convert(\$_,"euc"); } } sub simple { seek $fh, 0, 0; while (my $line = <$fh>) { $line = uri_unescape($line); Encode::from_to($line, 'utf8', 'eucjp'); } } sub fast { seek $fh, 0, 0; my $line; while (<$fh>) { $line = uri_unescape($_); my @items = split " ", $line; $items[11] = $out_enc->encode( $in_enc->decode($items[11]) ) if $items[11] =~ /\?/; } }
結果。
Rate Simple Legacy Split Simple 24.5/s -- -10% -56% Legacy 27.3/s 11% -- -51% Fast 55.7/s 127% 104% --
倍早くなったよ。