Class::MethodMaker in XS

IlyaM on 2003-04-02T10:02:44

I wonder why nobody yet tried to write something like Class::MethodMaker in XS? I tried to compare pure Perl accessor methods and their XS analogs and the latest was noticibly faster. It would be very nice if we had a CPAN module which provides basic constuctor, accessor and other common methods implemented in XS so everybody could benefit from these speed gains in their Perl OO code without resorting to writting XS themselves.


Indeed...

Matts on 2003-04-02T16:39:52

I thought about doing this a while ago. Another advantage being that you could get around perl's crappy OO system by using proper structs.

Unfortunately I stopped at the "thinking" stage :-/

Interesting idea

samtregar on 2003-04-02T18:17:47

So how would it work? Let's say I do:

  use Class::XSMethodMaker
     get_set => [qw(foo bar)];

Does this mean that XS code for foo() and bar() are generated and compiled on the fly? Or does it just do what Class::MethodMaker does and generate closures bound to *foo and *bar? Can you generate a closure in XS which closes around C code?

-sam

Re:Interesting idea

IlyaM on 2003-04-03T11:16:51

Tough question. One possible solution is pregenerating XS code given a list of fields. Another even generating C code on fly what should not be very hard using Inline::C. Anyway it doesn't seem a proper way to me and I'd like to be able simulate closures in C code. No idea how though. Not sure that it is even possible.

Why are they faster?

Dom2 on 2003-04-02T22:40:08

I was always under the impression that perl method dispatch was what caused things to take such a long time. If that's the case, then surely having methods execute faster once they've been dispatched to, wouldn't help much.

Or am I missing the point (or should that be the profile?) here?

-Dom

Re:Why are they faster?

IlyaM on 2003-04-03T11:08:09

Perl method dispatch is slow but perl code for accessors is not fast also. Here an example (one accessor is implemented in Perl and another in C with help of Inline::C):

use Benchmark;

my $obj = A->new;
$obj->field1('test');
$obj->field2('test');

timethese(1000000,
&nb sp;         { pure_perl => sub { $obj->field1($obj->field1) },
            inline_c  => sub { $obj->field2($obj->field2) }
          });

package A;

sub new { bless {}, shift }

sub field1 {
    my $self = shift;

    if(@_) {
        $self->{field1} = $_[0];
    }

    return $self->{field1};
}

use Inline C => <<'END';

SV* field2 (SV* self, ...) {
    SV** temp;
    Inline_Stack_Vars;

    if(!(SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV))
        croak("Not a hash reference");

    if(Inline_Stack_Items > 1) {
        SV* value = Inline_Stack_Item(1);

        if(! hv_store((HV*) SvRV(self), "field2", 6, SvREFCNT_inc(value), 0)) {
            SvREFCNT_dec(value);
            croak("Cannot store in hash");
        }
    }

    if(temp = hv_fetch((HV*) SvRV(self), "field2", 6, 0)) {
        return SvREFCNT_inc(*temp);
    } else {
        return &PL_sv_undef;
    }
}

END

Results on my computer:

Benchmark: timing 1000000 iterations of inline_c, pure_perl...
  inline_c:  2 wallclock secs ( 2.25 usr +  0.00 sys =  2.25 CPU) @ 444444.44/s
(n=1000000)
pure_perl:  6 wallclock secs ( 6.67 usr +  0.05 sys =  6.72 CPU) @ 148809.52/s
(n=1000000)