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.
----
----
----