ブログ  前の記事  次の記事  2007-03-07 

たけまる / 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>

一言メッセージをこっそり送信できます (非公開)
 今年の西暦→