XML::Parser(expat)使ってみたけれど
ベンチの内容は RSS の permalink を取り出すだけのものですが,
XML::LibXML や XML::Simple は一度 XML 木を生成しているのでちょっと無駄っぽい。XML::Parser のストリームタイプ(コールバックスタイル)で扱えば,がんばれば勝てるのではないかと思ってごりごり書いてみました。
15アイテムの RSS ファイルを元にやってみましたが…
Benchmark: timing 100 iterations of XML::LibXML, XML::Parser, XML::RSS, XML::Simple, regexp...
XML::LibXML: 1 wallclock secs ( 0.85 usr + 0.01 sys = 0.86 CPU) @ 116.28/s (n=100)
XML::Parser: 3 wallclock secs ( 2.84 usr + 0.01 sys = 2.85 CPU) @ 35.09/s (n=100)
XML::RSS: 68 wallclock secs (67.63 usr + 0.01 sys = 67.64 CPU) @ 1.48/s (n=100)
XML::Simple: 14 wallclock secs (14.18 usr + 0.15 sys = 14.33 CPU) @ 6.98/s (n=100)
regexp: 0 wallclock secs ( 0.04 usr + 0.00 sys = 0.04 CPU) @ 2500.00/s (n=100)
(warning: too few iterations for a reliable count)うーん,XML::LibXML には勝てなかったです。なぜか XML::RSS が律速になってるし。ちなみにバージョンは,
です。
ソースはこちら。
簡易ステートマシンを構築する必要があるので,XML::Parser の版だけモジュールにわけました。
package RetrievePermalink;
use strict;
use XML::Parser;
our $ONLY_COUNT_LINES = 0;
my $STATE_START = 0;
my $STATE_ITEM = 1;
my $STATE_LINK = 2;
my $state;
my @permalinks;
my $num_permalinks;
my @strings;
sub parse {
_get_parser()->parse(@_);
return $ONLY_COUNT_LINES ? $num_permalinks
: @permalinks ;
}
sub parsefile {
_get_parser()->parsefile(@_);
return $ONLY_COUNT_LINES ? $num_permalinks
: @permalinks ;
}
sub _get_parser {
@permalinks = ();
$num_permalinks = 0;
$state = $STATE_START;
return XML::Parser->new(
Handlers => {
Start => \&_elem_start,
End => \&_elem_end,
Char => \&_char,
},
);
}
sub _elem_start {
my ($expat, $element) = @_;
return unless $element eq 'item'
|| $element eq 'link';
if ($element eq 'item') {
return unless $state == $STATE_START;
$state = $STATE_ITEM;
}
elsif ($element eq 'link') {
return unless $state == $STATE_ITEM;
$state = $STATE_LINK;
@strings = ();
}
}
sub _elem_end {
my ($expat, $element) = @_;
return unless $element eq 'item'
|| $element eq 'link';
if ($element eq 'item') {
return unless $state == $STATE_ITEM;
$state = $STATE_START;
if (@strings) {
$num_permalinks ++;
if (! $ONLY_COUNT_LINES) {
push @permalinks, join('', @strings);
}
@strings = ();
}
}
elsif ($element eq 'link') {
return unless $state == $STATE_LINK;
$state = $STATE_ITEM;
}
}
sub _char {
my ($expat, $string) = @_;
return unless $state == $STATE_LINK;
push @strings, $string;
}
1;すんごい巨大な XML ファイルを食わせればメモリ効率の点でも XML::Simple 等に勝てると思いますが調べていません。