<Up>
I did an informal talk at the Boston Perl Mongers evening meeting of 2002-Mar-13. Here are the slides, quite rough, written in a few hours. They have assorted problems. (And this file has somewhat bizarre fonts, having not been postprocessed into slides.)
 

 

Perl Objects with C API's

or writing C extensions in an Inline world

 

 

Mitchell Charity
2002-Mar-13 Boston Perl Mongers
http://www.vendian.org/mncharity/dir3/inline/

----

Ok, show of hands...
How many people have used the Inline module?
Writen C extensions for perl?  Used XS?
Writen C code?
Used TIEARRAY?


Expectation management

 

This is a quick sketch of a work-in-progress.

----

Expectation management

Summary

      Just in time C coding.

----

 - 30 sec version of talk.  elevator talk.

 - a C extension is something written in C, which provides what look
   like normal perl objects

 - Inline makes it easier to use C from Perl.

 - Allows a running perl program to create, compile, and use C code,
   Rather than depending on some human having clicked at emacs months ago.

 - an application, rather than relying on a monolithic extension, can
   instead use a mix of small simple orthogonal objects, which together,
   create a tailored extension.  One which runs just as fast, or even
   faster than, a traditional extension.


Outline

----

 - how things are done now

 - what Inline is

 - an illustration of C apis

 - some possible impacts

Status Quo

----


 - Perl for productivity, C for speed
 
   - perl interpreter itself
   - various CPAN modules

 - ILLUSTRATE overhead
  So you have a function written in perl.
  You decide you want it written in C.
  So you write it in C.
  But now you have to put that code in a separate file,
  write glue code,
  run the compiler, and linker,
  and worry about managing the resulting library files.


C Extension Bloat

      C extensions which are
          big, monolithic, bloated, redundant, incomplete, incompatible

----



What is Inline?

----



Inline Example

"Compile" time

use Inline C => ' int times2(int a) { return a * 2 } ';
print &times2(8);
Runtime
sub make_multiplier {
  my($n) = @_;
  my $name =  "times$n";
  my $code =  " int ${name}(int a) { return a * $n; } ";
  Inline->bind(C => $code);
}

&make_multiplier(42);
print &times42(8);

----



Arrays - a prelude
sub FETCH { ... }

use Inline C => "SV* FETCH (int index) { ... } ";

use Inline C => "int FETCH (int index) { ... } ";

----



Example of a C API
Consider an example object, providing a pack()ed substr() array of int's:

  # For $Config{intsize} == 4
  sub TIEARRAY { my $string = "\0" x (4 * $_[1]); bless \$string,$_[0]; }
  sub FETCH { unpack("i", substr(${$_[0]},(4*$_[1]),4)); }
  sub STORE { substr(${$_[0]},(4*$_[1]),4) = pack("i",$_[2]); }

While more compact than a native perl array, accessing this via a
tie()d array is relatively much slower (though calling the methods
directly isn't too bad).

But even with a native array, groveling over a million elements is not
something one does with runtime impatience.
  sub do_something { for(my $i=0;$i<1000000;$i++) { $_[0]->STORE($i,42); } }
is not fast.

But our packed string object can _also_ provide a C API.
  sub get_C_API {
      return '
static void* get_ptr_from_object (SV* obj) { return SvPVX(SvRV(obj)); }
#define DECL(obj)      int* ptr = get_ptr_from_object(obj)
#define FETCH(idx)     ptr[idx]
#define STORE(idx,val) ptr[idx] = val
';
  }

And this
  use Inline;
  Inline->bind(C => 
    Example->get_C_API() . '
    void do_something2 (SV* obj) {
      DECL(obj);
      int i;
      for(i=0;i<1000000;i++) { STORE(i,42); }
    }
    ');
is blazingly fast.

----



Perl Objects with C API's
package FirstClass;

sub foo { ... }

sub get_C_api { return "#define FAST_FOO(arg) /* ... */ \n"; }

package SecondClass;
@ISA = 'FirstClass';

sub get_C_api {
  my $code = FirstClass->get_C_api();
  $code =~ s/FAST_FOO/SUPER_FOO/;
  return $code . "#define FAST_FOO(arg) mumble(SUPER_FOO(arg))\n";
}

sub foo { ... return mumble($self->SUPER::foo(@_)); }

use Inline C => FirstClass->get_C_api() .
   "SV* another_foo(SV* arg) { return mumble(FAST_FOO(arg)); }";

----



Perl Objects with C API's - Continued
package FooWithFrob_traditional;

sub new { bless { a_foo => $_[1] }, $_[0]; }

sub frob { mutter(shift->{'a_foo'}); }


package FooWithFrob_newfangled;

sub frob { shift->{'optimized_frob'}->(@_); }

sub new {
  my($class,$a_foo) = @_;

  my $foo_api = $a_foo->get_C_api();
  my $id      = md5sum($foo_api);
  my $fname   = "optimized_frob_${id}";

  Inline->bind(C => $foo_api .
      "SV* ${fname}() { ... mutter ... FAST_FOO(...) ... }");

  my $self = { a_foo => $a_foo,
               optimized_frob => \&$fname };
  bless $self,$class;
}


----

 - and with prototyped based object systems

Using arrays - Sampling
sub FETCH { ...->[ $index * 3 ]; }

sub FETCH { ...->FETCH( index * 3 ); }

----

 - method call, high cost of bouncing through a tie.


Array Sampling with a C API
package ClassA;
sub FETCH { ... }
sub get_api_TIEARRAY_C {
  ...
  return "
#define ${prefix}USING(object) ...
#define ${prefix}FETCH(index)  ...
...
"; }

package ClassB;
@ISA = 'ClassA';

sub get_api_TIEARRAY_C {
  return (ClassA->get_api_TIEARRAY_C(prefix => 'under')
          . "
#define USING(object) underUSING(object)
#define FETCH(index)  underFETCH(index * 3)
");

use Inline C => (ClassB->get_api_TIEARRAY_C(prefix => 'myown')
                 . 'SV* FETCH (SV* self, int index) {
                       myownUSING(self);
		       return myownFETCH(index);
                    }');

----



Another example

A generic function on two arrays:

sub an_operation_on_two_arrays {
   my($a1,$a2) = @_;
   my $function_name = "my_helper_" . md5sum($a1.$a2);
   Inline->bind(  $a1->get_api_TIEARRAY(prefix => 'one')
                . $a2->get_api_TIEARRAY(prefix => 'two')
                . "SV* $function_name (SV* a1, SV* a2)
                   {
                     /* code implementing my operation on two arrays */
                   }");
   return &$function_name($a1,$a2);
}

----

 - a cruftier version also works on anything which looks like a native array.

Perl Objects with C API's

----


 - C

Limitations

----

 - re c/perl interaction
   nature of C friendly data
     need not be C side (eg, pack)
     dual access functions



Who cares?

----

(potential impact on software engineering)


Beyond image libraries

----


 - Most images are just axis-aligned, descrete-grid, 3D-boxes
 ("cuboids") of small non-negative integers.


Misc

----



----



----