XML::RSS::LibXML を使って続・Perl で XML の処理ベンチ
XML::RSS::LibXML uses XML::LibXML (libxml2) for parsing RSS instead of XML::RSS' XML::Parser (expat), while trying to keep interface compatibility with XML::RSS.
CPAN に XML::RSS::LibXML というモジュールが新着で上がっていました。XML::RSS は内部で expat を使う XML::Parser を XML パーザーに使っています。が、これよりも libxml2 を使う XML::LibXML の方が速くて効率が良い、ということで XML::RSS のインタフェースはそのままにパーザーを XML::LibXML に置き換えたのがこのモジュールです。(ちなみに 100% XML::RSS 互換ではないとの注意書きあり。) 作者も Daisuke Maki 氏なので安心して使えそう。
さて、随分と前にPerl で XML の処理はどれが速いかベンチなんてことを書いたのですが、XML::RSS::LibXML を使って同じベンチマークを実行してみたくなるのが Hacker ってもんです。
早速やってみました。
Benchmark: timing 1000 iterations of XML::LibXML, XML::RSS, XML::RSS::LibXML... XML::LibXML: 2 wallclock secs ( 1.94 usr + 0.00 sys = 1.94 CPU) @ 515.46/s (n=1000) XML::RSS: 114 wallclock secs (107.22 usr + 0.13 sys = 107.35 CPU) @ 9.32/s (n=1000) XML::RSS::LibXML: 10 wallclock secs (10.50 usr + 0.03 sys = 10.53 CPU) @ 94.97/s (n=1000)
おおー、速い! 生で XML::LibXML を使うよりも多少のオーバーヘッドがあるは当然ですが、XML::RSS と XML::RSS::LibXML には 10 倍の差がありました。ちょっとベンチマークの処理が単純すぎるのですが、目安にはなりそうです。
ちなみに、ソースは以下。前回のソースに XML::RSS::LibXML 部分を足して一部コメントアウトしただけです。
#!/usr/local/bin/perl use strict; use warnings; use Benchmark; use FileHandle; use XML::LibXML; use XML::RSS; use XML::RSS::LibXML; use XML::Simple; my $rss_file = shift or die "usage $0 <rss_file>\n"; my $fh = FileHandle->new($rss_file) or die "cannot open $rss_file: $!"; local $/; # slurp mode our $content = $fh->getline; $fh->close; Benchmark::timethese(1000, { # 'regexp' => \&with_regexp, # 'XML::Simple' => \&with_xml_simple, 'XML::RSS' => \&with_xml_rss, 'XML::LibXML' => \&with_xml_libxml, 'XML::RSS::LibXML' => \&with_xml_rss_libxml, }); sub with_regexp { my $pattern = "<item .*?>.*?<link>(.*?)</link>.*?</item>"; my @links = ($content =~ m/$pattern/smg); } sub with_xml_simple { my @links = (); my $parser = XML::Simple->new; my $data = $parser->XMLin($content, ForceArray => 1); for my $item (@{$data->{item}}) { push @links, $item->{link}->[0]; } } sub with_xml_rss { my @links = (); my $rss = XML::RSS->new; $rss->parse($content); for my $item (@{$rss->{items}}) { push @links, $item->{link}; } } sub with_xml_libxml { my @links =(); my $parser = XML::LibXML->new; my $doc = $parser->parse_string($content); my @nodes = $doc->findnodes( "//*[local-name()='item']/*[local-name()='link']/text()" ); for my $node (@nodes) { push @links, $node->nodeValue; } } sub with_xml_rss_libxml { my @links = (); my $rss = XML::RSS::LibXML->new; $rss->parse($content); for my $item (@{$rss->{items}}) { push @links, $item->{link}; } }