たけまる / Perl - XML の処理はどれが速いかベンチ
2007-03-07
_
Perl - XML の処理はどれが速いかベンチ [perl][xml]



XML を多量に読み書きする予定なので,処理速度を調べてみることにしました.
パース速度については,昔の naoya さんの記事が参考になります.
Perl で XML の処理はどれが速いかベンチ : NDO::Weblog
RSS を対象としたベンチマークでは,正規表現が最速,次いで
XML::LibXML とのことです.
XML::Atom の性能を知りたかったので,同じ試験を Atom でやってみまし
た.XML::Atom は内部で XML::LibXML を使っています.
Benchmark: timing 10000 iterations of XML::Atom, XML::LibXML, XML::Simple, regexp...
XML::Atom: 4 wallclock secs ( 3.58 usr + 0.00 sys = 3.58 CPU) @ 2793.30/s (n=10000)
XML::LibXML: 2 wallclock secs ( 2.22 usr + 0.00 sys = 2.22 CPU) @ 4504.50/s (n=10000)
XML::Simple: 52 wallclock secs (51.03 usr + 0.48 sys = 51.51 CPU) @ 194.14/s (n=10000)
regexp: 0 wallclock secs ( 0.06 usr + 0.00 sys = 0.06 CPU) @ 166666.67/s (n=10000)
(warning: too few iterations for a reliable count)
テストデータには,entry をひとつだけ含むシンプルなものを使いました.
XML::Atom の速度は,XML::LibXML + overhead くらいの結果となりまし
た.保守性を考えると,XML::Atom を拡張して使うのは悪くなさそうです.
さて,出力処理は軽いので気にしなくてよいと思うのですが,念のため確
認してみました.比較対象は XML::Simple, XML::Atom, TT です.
TT: 11 wallclock secs ( 3.77 usr + 0.19 sys = 3.96 CPU) @ 2525.25/s (n=10000)
XML::Atom: 10 wallclock secs ( 9.80 usr + 0.00 sys = 9.80 CPU) @ 1020.41/s (n=10000)
XML::Simple: 6 wallclock secs ( 6.26 usr + 0.00 sys = 6.26 CPU) @ 1597.44/s (n=10000)
パースほど差はつきませんでしたが,TT がやや速い結果となりました.
TT と XML::Simple は名前空間などのオーバヘッドがないと思うので,そ
の分速いのでしょう.実装がラクなことを考えても,TT がベストの選択
肢になりそうです.
XML::Atom は,意外にもパースのが速かったりします.
以下は,ベンチマークスクリプトです.
parse.pl
use strict;
use warnings;
use Benchmark;
use FileHandle;
use XML::LibXML;
use XML::Atom::Feed;
use XML::Simple;
my $atom_file = shift or die "usage $0 \n";
my $fh = FileHandle->new($atom_file)
or die "cannot open $atom_file: $!";
local $/; # slurp mode
our $content = $fh->getline;
$fh->close;
Benchmark::timethese(10000, {
'regexp' => \&with_regexp,
'XML::Simple' => \&with_xml_simple,
'XML::Atom' => \&with_xml_atom,
'XML::LibXML' => \&with_xml_libxml,
});
sub with_regexp {
my $pattern = "<entry.*?>.*?<link.*?href=\"(.*?)\".*?</entry>";
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 $entry (@{$data->{entry}}) {
push @links, $entry->{link}[0]{href};
}
}
sub with_xml_atom {
my @links = ();
my $atom = XML::Atom::Feed->new(\$content);
for my $entry ($atom->entries) {
push @links, $entry->link->href;
}
}
sub with_xml_libxml {
my @links =();
my $parser = XML::LibXML->new;
my $doc = $parser->parse_string($content);
my @attrs = $doc->findnodes(
"//*[local-name()='entry']/*[local-name()='link']/\@href"
);
for my $attr (@attrs) {
# print $node->nodeValue, "\n";
push @links, $attr->getValue;
}
}
build.pl
use strict;
use warnings;
use Benchmark;
use XML::Simple;
use XML::Atom::Entry;
use XML::Atom::Content;
use Template;
$XML::Atom::DefaultVersion = "1.0";
$XML::Atom::ForceUnicode = 1;
XML::Atom::Content->mk_attr_accessors(qw( src ));
my $tt = Template->new || die;
Benchmark::timethese(10000, {
'XML::Simple' => \&with_xml_simple,
'XML::Atom' => \&with_xml_atom,
'TT' => \&with_tt,
});
sub with_xml_simple {
my $hash = {
version => '1.0',
encoding => 'utf-8',
entry => {
xmlns => 'http://www.w3.org/2005/Atom',
title => {
'$t' => 'Foo Bar',
},
id => {
'$t' => 'ID',
},
updated => {
'$t' => '2007-01-01T00:00:00',
},
link => {
rel => 'edit',
href => 'http://bar.com/1/2/',
},
category => {
term => 'foo',
scheme => 'http://foo.com/#scheme',
},
author => {
name => {
'$t' => 'Foo Bar',
},
},
content => {
src => 'http://bar.com/_/2/',
},
published => {
'$t' => '2007-01-01T00:00:00',
},
},
};
return "<?xml version=\"$hash->{version}\" encoding=\"$hash->{encoding}\"?>\n"
. XMLout({entry => $hash->{entry}}, KeepRoot => 1, ContentKey => '$t');
}
sub with_xml_atom {
my $link = XML::Atom::Link->new || die;
$link->rel('edit');
$link->href('http://foo.com/1/2/');
my $category = XML::Atom::Category->new || die;
$category->term('foo');
$category->scheme('http://foo.com/#scheme');
my $author = XML::Atom::Person->new || die;
$author->name('Foo Bar');
my $content = XML::Atom::Content->new || die;
$content->src('http://foo.com/_/2/');
my $entry = XML::Atom::Entry->new || die;
$entry->title('Foo Bar');
$entry->id('ID');
$entry->updated('2007-01-01T00:00:00');
$entry->add_link($link);
$entry->category($category);
$entry->author($author);
$entry->content($content);
$entry->published('2007-01-01T00:00:00');
return $entry->as_xml || die;
}
sub with_tt {
my $hash = {
version => '1.0',
encoding => 'utf-8',
entry => {
xmlns => 'http://www.w3.org/2005/Atom',
title => 'Foo Bar',
id => 'ID',
updated => '2007-01-01T00:00:00',
link => {
rel => 'edit',
href => 'http://bar.com/1/2/',
},
category => {
term => 'foo',
scheme => 'http://foo.com/#scheme',
},
author => {
name => 'Foo Bar',
},
content => {
src => 'http://bar.com/_/2/',
},
published => '2007-01-01T00:00:00',
},
};
my $out = '';
$tt->process(\*DATA, $hash, \$out) || die;
return $out;
}
__DATA__
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="[% entry.xmlns -%]">
<title>[% entry.title -%]</title>
<id>[% entry.id -%]</id>
<updated>[% entry.updated -%]</updated>
<link rel="[% entry.link.rel -%]" href="[% entry.link.href -%]" />
<category term="[% entry.category.term -%]" scheme="[% entry.category.scheme -%]" />
<author src="[% entry.author.src -%]" />
<content src="[% entry.content.src -%]" />
<published>[% entry.published -%]</published>
</entry>
