Comments on "Performance Comparison Between SAX XML::Filter::Dispatcher and XML::Twig"
These comments refer to Tom Anderson's Performance Comparison Between SAX XML::Filter::Dispatcher and XML::Twig article.
It's an interesting article. I especially never realized that returning a short (1) value from a handler could impact performances that much (see difference between test 2 and test 3 below).
The code presented is quite simple, it could be slightly simplified by replacing $_[1] by simply $_ in handlers (from the doc "$_ is also set to the element, so it is easy to write inline handlers like para => sub { $_->change_gi( 'p'); }.
There is one problem with the code though: using $_[1]->{att}->{val}: attributes should be accessed using the provided accessor method $_[1]->att( 'val'). This impacts performances, but the faster syntax is not garanteed to work in a future version of the module (for example if I decide to pool attribute values, of course I might implement it as a tied-hash, but there is no garantee for that in the documentation of the module).
I tried to squeeze some additionnal speed on the example, using twig_roots so the tree is not build for most elements. In this case this kinda defeats the purpose of XML::Twig and turns it essentially into a rather thin layer on top of XML::Parser, but at least it takes care of dispatching the callbacks without using "large case statements".
The code for the most interesting test (test 3) is this:
#!/usr/bin/perl # # Program to test performance of XML::Twig # Tom Anderson + mirod # Thu Jan 16 22:49:24 PST 2003 + Friday March 7 2003 # same as test_perf2 except handlers return 1 # use strict; use warnings; use diagnostics; use XML::Twig; use File::Slurp; my $VERSION=0.02; my $out_file= shift( @ARGV) || "$0.out"; my $less_memory=1; my $out=""; my $xml= XML::Twig->new( # start_tag_handlers are called when the start tag is found # if the element is outside of the twig_roots then parameters are similar to XML::Parser's: # the twig, the gi and a hash of attributes start_tag_handlers => { '/TRACES/NET/WIR' => sub { my( $t, $gi, %atts)= @_; $out .= 'WIR '. $atts{numseg} .' '. $atts{startx} .' '. $atts{starty} .' '. $atts{termx} .' '. $atts{termy} .' '. $atts{optgroup}."\n"; 1; }, '/TRACES/NET' => sub { my( $t, $gi, %atts)= @_; $out .= "# NET '". $atts{name}."'\n"; 1; }, '/TRACES/UNITS' => sub { my( $t, $gi, %atts)= @_; $out .= 'UNITS '. $atts{val}."\n"; 1; }, '/TRACES/STFIRST' => sub { my( $t, $gi, %atts)= @_; $out .= 'ST '. $atts{maxx} .' '. $atts{maxy} .' '. $atts{maxroute}.' '. $atts{numconn} ."\n"; 1; }, '/TRACES/XRF' => sub { my( $t, $gi, %atts)= @_; $out .= 'XRF '. $atts{num} .' '. $atts{name}."\n"; 1; }, '/TRACES/NET/WIR/SEG' => sub { my( $t, $gi, %atts)= @_; $out .= 'SEG '. $atts{x} .' '. $atts{y} .' '. $atts{lay} .' '. $atts{width}."\n"; 1; }, '/TRACES/NET/GUI' => sub { my( $t, $gi, %atts)= @_; $out .= 'GUI '. $atts{startx} .' '. $atts{starty} .' '. $atts{startlay}.' '. $atts{termx} .' '. $atts{termy} .' '. $atts{termlay} .' '. $atts{optgroup}."\n"; 1; }, '/TRACES/STLAST' => sub { my( $t, $gi, %atts)= @_; $out .= 'ST '. $atts{checkstat} .' '. $atts{numcomplete}.' '. $atts{numinc} .' '. $atts{numunroute} .' '. $atts{numnotrace} .' '. $atts{numfill} ."\n"; 1; }, }, # the twig will be built only for those elements twig_roots => { '/TRACES/HEADER' => sub { $out .= $_->text; $_[0]->purge if $less_memory; # not really useful in this case, there is only 1, small HEADER element 1; }, }, ); $xml->parsefile('traces.xml'); write_file( $out_file, $out); |
All tests: sax_twig_comments.tar.gz, using data from http://tomacorp.com/perl/xml/traces.xml (unpack, save traces.xml in the sax_twig_comments directory and run with run_test_perf)
Here are the results:
Test 1 - original version (test_perf1) 2 wallclock secs (0.00 usr 0.00 sys + 1.63 cusr 0.09 csys = 1.72 CPU) Test 2 - using twig_roots (test_perf2) 4 wallclock secs (0.00 usr 0.00 sys + 2.76 cusr 0.97 csys = 3.73 CPU) Test 3 - twig_roots + return 1 (test_perf3) 0 wallclock secs (0.01 usr 0.00 sys + 0.72 cusr 0.03 csys = 0.76 CPU) Test 4 - twig_roots + return undef (test_perf4) 1 wallclock secs (0.00 usr 0.00 sys + 0.71 cusr 0.05 csys = 0.76 CPU) Test 5 - handlers on element name (test_perf5) 1 wallclock secs (0.00 usr 0.00 sys + 0.74 cusr 0.01 csys = 0.75 CPU) Test 6 - same with partial path (test_perf6) 1 wallclock secs (0.00 usr 0.00 sys + 0.70 cusr 0.04 csys = 0.74 CPU)
Note that results for test 5 and 6 vary, sometimes one is faster than the other, sometimes it's the opposite.
© Michel Rodriguez - 2003