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?
This is a quick sketch of a work-in-progress.
----
Expectation management
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.
----
- how things are done now - what Inline is - an illustration of C apis - some possible impacts
----
- 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 extensions which are
big, monolithic, bloated,
redundant, incomplete, incompatible
----
----
"Compile" time
use Inline C => ' int times2(int a) { return a * 2 } ';
print ×2(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 ×42(8);
----
sub FETCH { ... }
use Inline C => "SV* FETCH (int index) { ... } ";
use Inline C => "int FETCH (int index) { ... } ";
----
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.
----
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)); }";
----
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
sub FETCH { ...->[ $index * 3 ]; }
sub FETCH { ...->FETCH( index * 3 ); }
----
- method call, high cost of bouncing through a tie.
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);
}');
----
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.
----
- C
----
- re c/perl interaction
nature of C friendly data
need not be C side (eg, pack)
dual access functions
----
(potential impact on software engineering)
----
- Most images are just axis-aligned, descrete-grid, 3D-boxes
("cuboids") of small non-negative integers.
----
----
----