February 2014 Archives

Perl 5.19.x performance improvements

Perl 5.19.9 was released today, and it includes some exciting new features like subroutine signatures, and postfix dereferencing.

The 5.19.X line also includes a number of optimisations that I'd like to discuss briefly, without going into too much detail (I hope).

Combined and/or operators in void context

The first improvement was added back in 5.19.6, and effects and and or operators in void context. It improves the short-cirtcuiting logic of and/or operations when inside of an if statement.

What this means is that in many cases, this:

if ($var1 || $var2) { ... }

will be ever-so-slightly faster if $var1 is true than it used to, and

unless ($var1 && $var2) { ... }

will be ever-so-slightly faster if $var1 is false. This is about a 2% speed improvement in my tests, for each of the above constructs being evaluated.

return at end of sub optimised out

This improvement was added in 5.19.8, and optimises out a return statement at the end of a sub so that:

return $x;

is compiled as

$x;

which is a fair bit faster (about 10%) since a return statement actually includes a few more ops behind the scenes than necessary for these cases.

Multiple single PADOPs combined into a list

The final optimisation was just released with 5.19.9 and turns multiple single PADOPs into its list form.

That means this:

my $x;
my $y;

becomes this:

my ($x, $y);

Which then can be optimised into a single PADRANGE op which was added back in 5.18.0.

This gives a speedup of about 7% in the above case, and will have a greater effect if more PADOPS (my $x; my @y; my %z) are used in a row.

Testing

For light testing, I used a best-case scenario subroutine for all above optimisations, and enabled each in turn to see how the sub improved. (I didn't test them individually, so numbers may vary. IE: I enabled return optimisations, then padrange optimisations, then and/or in void context optimisations).

Using this program:

#!/usr/bin/perl
use strict;
use warnings;

my $aa = 1;
my $bb = 0;

sub somesub {
        my $x;
        my $y;

        if ($aa || $bb) {
                $x = 1;
        }

        return $x;
}

for (1..50_000_000) {
        somesub();
}

I ran it 3 times for each test using "time ./opt.pl" (I know, start up costs skew results, but only ever so slightly in comparison to what was being tested in this case, and yes, 3 is not enough to give a really good average...), and averaged out the results:

Original:                                      6.69s
Optimise out return:                           6.03s   10% speedup
Optimise my $x; my $y; into my ($x; $y);:      5.62s    7% speedup
Optimise if ($a || $b) { ... } if $a is true:  5.53s    2% speedup

Total:                                                 19% speedup

I'm pretty happy with the results.

Playing with Perl's optimiser was a lot of fun (and a fair amount of frustration :)), and I hope to do more work in this area.

Perl 5.20 (which is set to release May of this year) will includes these optimisations, and I hope to have many more for 5.22 next year.

Cheers,

-- Matthew Horsfall (alh)

P.S. If anyone wants more in-depth details of the optimisations, I'm happy to provide them. (Separate blog posts?)

About Matthew Horsfall (alh)

user-pic I blog about Perl.