Date: Fri, 25 Jan 2002 18:54:10 -0500 To: perl5-porters@perl.org Subject: Inline::C and Perl objects with C API's From: Mitchell N Charity Message-Id: <200201252354.g0PNsAA29146@vendian.org> Here is a quick sketch of the motivation for some modules I am working on. 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. The first invocation may completely pay for gcc's optimizing compile. And with this option of using an api which compiles inline, subsequent calls can be faster than even a pure C application of hand-tuned code calling out to a traditional library of C functions. Which gives us an alternative. Both to the usual practice of extending of Perl by creating C libraries capped by shallow and library-specific interface objects; and to the hairy games people play with C++ templates, as they try to coerce preprocessors into providing this `everything is compiled together'-ness (to remove abstraction barrier costs from high performance C libraries). Better yet, we can now pile our abstractions deep and rich, and cleanly engineer them back to high performance. Without bondage languages. Or blind faith in magically omniscient compilers. Or heroic quests after portable and powerful C++ templates. Perl is a darn fine place to write C (and C++) code. No surprise there. Mitchell Charity (and off to dinner...) ----[]---- package Example; # Much simplified for clarity, but functional. use Inline; # 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]); } sub do_something { for(my $i=0;$i<1000000;$i++) { $_[0]->STORE($i,42); } } 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 '; } sub create_do_something2 { 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); } } '); } package main; my(@a,@b); tie @a, 'Example', 1000000; print STDERR "tie()d array..."; for(my $i=0;$i<1000000;$i++) { $a[$i] = 42; } print STDERR "done.\nmethod calls..."; tied(@a)->do_something(); print STDERR "done.\nC API..."; tied(@a)->create_do_something2(); tied(@a)->do_something2(); print STDERR "done.\nnative perl array..."; for(my $i=0;$i<1000000;$i++) { $b[$i] = 42; } print STDERR "done.\n"; print STDERR "\n Now, if you've only run this once,". " try it again and watch the C API time. Delete your _Inline cache directory to repeat.\n"; __END__ ----[]----