The following just went out to perl5-porters.

What fun,
Mitchell Charity


To: [EMAIL PROTECTED]
Subject: Inline::C and Perl objects with C API's
From: Mitchell N Charity <[EMAIL PROTECTED]>
Reply-to: [EMAIL PROTECTED]

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__
----[]----

Reply via email to