Begin main content

Optimising Perl with Inline::C

I was discussing with someone today about a time I used Inline::C to massively speed up an inner loop in a Perl program. Thing is, in that case the real speedup wasn't any super smart C programming on my behalf, it was just making use of a very optimised vendor library that you could only access from C.

So I got to thinking - in normal every day code, is there any real speed benefit to be had by writing your inner loops in C.

I found an old web page by Mitchell Charity discussing Inline and, interpreting his (slightly pathological) example a little, I got a surprising result:

use strict;
use warnings;

use Inline 'C';
use Benchmark qw(cmpthese);

cmpthese( 50,
         {
             perl_method => sub {
                 my $object = new Foo;

                 for(my $i=0;$i<1_000_000;$i++) {
                     $object->set_element($i,67);
                 }
             },

             all_in_one_c => sub {
                 my $object = new Foo;
                 
                 set_all_with_c($object);
             }
           
         });

package Foo;

sub new { my $self = " " x 1_000_000; return bless \$self, 'Foo' }

sub set_element {
  my($self,$n,$value) = @_;
  substr($$self,$n,1) = pack("C",$value);
}

__END__
__C__

#define USING(object) unsigned char *ptr = SvPVX(SvRV(object))
#define SET_ELEMENT(IDX,VAL) ptr[IDX] = VAL

void set_all_with_c (SV* object) {
    USING(object);
    int i;
    for(i=0;i<1000000;i++) { SET_ELEMENT(i, 67); }
}
               s/iter  perl_method all_in_one_c
perl_method      4.16           --        -100%
all_in_one_c 8.60e-03       48321%           --
ie. the C code was 48321% times as fast.

But it's not really comparing apples with apples - the Perl code is doing a method call on each iteration, the C code is operating on the value directly. In addition, the C code (by way of Inline::C's magic) is basically copying the string into a temporary variable, operating directly on that, and copying back - so the dereferencing is not happening on each loop. We can make those changes in Perl too, and see how that compares.

...

sub set_all_with_perl {
    my $tmp_str = ${ $_[0] };
    substr($tmp_str, $_, 1) = pack("C",67) for 0..1_000_000;
    ${ $_[0] } = $tmp_str;
}

...
                  s/iter     perl_method all_in_one_perl    all_in_one_c
perl_method         4.21              --            -62%           -100%
all_in_one_perl     1.61            161%              --            -99%
all_in_one_c    8.60e-03          48821%          18626%              --
So eliminating the method dispatch and dereference in the loop made our Perl code much faster, but the C code is still way faster. Obviously it's a contrived example, and 1 million iterations is one heck of an inner loop, but I am still surprised by how much difference it made.

01:38 AM, 11 Jan 2008 by Mark Aufflick Permalink | Short Link | Comments (0)

XML

Blog Categories

software (27)
..cocoa (13)
  ..heads up 'tunes (5)
..ruby (4)
..lisp (1)
..perl (3)
..openacs (1)
mac (18)
embedded (2)
..microprocessor (2)
  ..avr (1)
electronics (3)
design (1)
photography (24)
..black and white (6)
..A day in Sydney (18)
..The Daily Shoot (5)
food (2)

Notifications

Icon of Envelope Request notifications

Syndication Feed

XML

Recent Comments

  1. Unregistered Visitor: To replace current categories or remove them...
  2. Unregistered Visitor: Base 256
  3. Unregistered Visitor: ...
  4. Unregistered Visitor: www.pipl.com
  5. Unregistered Visitor: This reminds me of the infinite monkeys
  6. Unregistered Visitor: Feedback
  7. Unregistered Visitor: Diito... Snow Leopard no worky
  8. Unregistered Visitor: WFM
  9. Unregistered Visitor: Pie
  10. Unregistered Visitor: Helpful