making Perl command-line scripts faster with pperl

So, you have a script which is slow, perhaps because you are using a whole collection of modern perl features, which aren't necessarily terribly fast yet. You can't wait for the runtime to implement the features natively and hence run quickly, but there is another solution.

For instance, the XML::SRS distribution on CPAN makes use of some fairly advanced features of Moose, such as meta-attribute meta-roles. These are a win from a coding and maintenance point of view, as they allow a single attribute declaration to give you a Perl class which has XML marshalling as well as type constraints. However it does have a high startup penalty.

How high? Let's try by, say, taking a script which takes a JSON document on input and passes that to a Moose constructor, then outputting the XML.

#!/usr/bin/perl
use XML::SRS;
use JSON::XS;
my $json = join "", <>;
print XML::SRS::Domain::Create->new(
    %{decode_json($json)}
)->to_xml(1);

Fairly simple, right? Now, let's pass into that the data structure from the SYNOPSIS on the man page to JSON, and see how quickly it runs:

$ json=\
'{"domain_name":"kaihoro.co.nz","contact_registrant":{"email":\
"kaihoro.takeaways@gmail.com","name":"Lord Crumb","address":{\
"city":"Kaihoro","cc":"NZ","region":"Nelson","address1":\
"57 Mount Pleasant St","address2":"Burbia"},"phone":{"subscriber":\
"499 2267","ndc":"4","cc":"64"}},"delegate":1,"nameservers":[\
"ns1.registrar.net.nz","ns2.registrar.net.nz"],"action_id":\
"kaihoro.co.nz-create-1298944261","term":12}'
$ echo $json | time ./test.pl
<?xml version="1.0" encoding="ISO-8859-1"?>
<DomainCreate Delegate="1" ActionId="kaihoro.co.nz-create-1298944261" DomainName="kaihoro.co.nz" Term="12">
  <RegistrantContact Name="Lord Crumb" Email="kaihoro.takeaways@gmail.com">
    <PostalAddress Address2="Burbia" Address1="57 Mount Pleasant St" Province="Nelson" City="Kaihoro" CountryCode="NZ"/>
    <Phone LocalNumber="499 2267" AreaCode="4" CountryCode="64"/>
  </RegistrantContact>
  <NameServers>
    <Server FQDN="ns1.registrar.net.nz"/>
    <Server FQDN="ns2.registrar.net.nz"/>
  </NameServers>
</DomainCreate>
1.14user 0.03system 0:01.19elapsed 98%CPU (0avgtext+0avgdata 113152maxresident)k
0inputs+0outputs (0major+7174minor)pagefaults 0swaps

Ok, so the script ran in 1.14s that time. Not exactly a speed demon!

But if we change one line in the script:

#!/usr/bin/perl

to:

#!/usr/bin/pperl

Then we get a much different time the second time that the script is run:

$ echo $json | time ./test.pl
<?xml version="1.0" encoding="ISO-8859-1"?>
<DomainCreate Delegate="1" ActionId="kaihoro.co.nz-create-1298944261" DomainName="kaihoro.co.nz" Term="12">
  <RegistrantContact Name="Lord Crumb" Email="kaihoro.takeaways@gmail.com">
    <PostalAddress Address2="Burbia" Address1="57 Mount Pleasant St" Province="Nelson" City="Kaihoro" CountryCode="NZ"/>
    <Phone LocalNumber="499 2267" AreaCode="4" CountryCode="64"/>
  </RegistrantContact>
  <NameServers>
    <Server FQDN="ns1.registrar.net.nz"/>
    <Server FQDN="ns2.registrar.net.nz"/>
  </NameServers>
</DomainCreate>
0.00user 0.00system 0:00.17elapsed 2%CPU (0avgtext+0avgdata 4704maxresident)k
0inputs+0outputs (0major+376minor)pagefaults 0swaps
$

Great! Down to 170ms! That's much more like an acceptable start-up time :-). Knowing the code base I happen to know that there is a lot of lazy evaluation which is responsible for a lot of that 170ms, so this could probably be improved upon. But a >80% total improvement is a pretty big win for adding a single character to the script.

That's one of the reasons I like Perl. It might suck for a number of reasons, but hey most languages suck for some reason or the other, and at least with Perl there's already a bunch of solutions available, either on CPAN or (in this case) Debian/Ubuntu.