Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Accessors in xs

by hardburn (Abbot)
on Oct 24, 2005 at 17:11 UTC ( #502523=perlquestion: print w/replies, xml ) Need Help??

hardburn has asked for the wisdom of the Perl Monks concerning the following question:

This question is half Perl, half perl, and half C. That's too many halves, but I was never very good at math.

I have some accessors that, after profiling, seem to be good candidates for some efficiency gains. Even shaving a few fractions of a second off could make a big difference, as they tend to get called a lot.

Here's the Perl implementation of the one I'm starting with:

sub world_location { my ($self) = @_; return ($self->{x}, $self->{y}); }

Which works just fine according to my test scripts. Now in xs:

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" AV* world_location( SV* self ) { HV *hash_self = (HV*) SvRV( self ); SV **x = hv_fetch( hash_self, "x", 1, FALSE ); SV **y = hv_fetch( hash_self, "y", 1, FALSE ); AV *to_return = newAV(); av_push( to_return, *x ); av_push( to_return, *y ); return to_return; } MODULE = My::Games::Azure::Unit PACKAGE = My::Games::Azure::Unit PROTOTYPES: DISABLE AV* world_location(self) SV* self

However, my test script gives me the following failures:

NOK 1# Failed test (t/025_unit.t at line 128) # got: 140725396 # expected: 84 t/025_unit...........NOK 2# Failed test (t/025_unit.t at line 129) + # got: undef # expected: 84

(The first test is the X coord, and the second is the Y coord.)

The values returned seem to be identical in each run. I haven't done much xs and my C is rusty, so I'm not sure what I'm doing wrong here.

"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Replies are listed 'Best First'.
Re: Accessors in xs
by BrowserUk (Patriarch) on Oct 24, 2005 at 18:05 UTC

    No XS-pert here either, but it looks to me like you are returning an AV*, ie. a ref to an array, rather than a list. You don't show the test code, but if you're doing

    my( $x, $y ) = $obj->world_location;

    That could be your problem?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      That was it. Applying chromatic's suggestion for a *PUSH* macro fixed it all:

      void world_location(self) SV* self PREINIT: HV *hash_self = (HV*) SvRV( self ); SV **x = hv_fetch( hash_self, "x", 1, FALSE ); SV **y = hv_fetch( hash_self, "y", 1, FALSE ); PPCODE: EXTEND(SP, 2); PUSHs( *x ); PUSHs( *y );

      Passes all tests. Thanks.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Re: Accessors in xs
by chromatic (Archbishop) on Oct 24, 2005 at 17:48 UTC

    I'm not sure I understand the use of an AV there; if it were me, I would just push the return values on the stack with one of the *PUSH* macros. Did you try that?

Re: Accessors in xs
by robin (Chaplain) on Oct 24, 2005 at 19:06 UTC
    There are several problems with your code. As others have said, you're returning one array-ref instead of two scalars, which accounts for your test failures. But there are also some more subtle problems which could cause core dumps if the caller does something wrong. I would write it as:
    #include "EXTERN.h" #include "perl.h" #include "XSUB.h" MODULE = My::Games::Azure::Unit PACKAGE = My::Games::Azure::Unit PROTOTYPES: DISABLE void world_location(self) HV *self PREINIT: SV **x, **y; PPCODE: x = hv_fetch( self, "x", 1, 0 ); y = hv_fetch( self, "y", 1, 0 ); EXTEND(SP, 2); PUSHs(x ? *x : &PL_sv_undef); PUSHs(y ? *y : &PL_sv_undef);
    The things to notice are:
    1. The self parameter is declared as an HV*, which causes the XS wrapper to issue automatic conversion and type checking code, so you'll get an error if someone accidentally calls it as My::Games::Azure::Unit->world_location() or whatever.
    2. The SV** pointers returned by hv_fetch() are tested to see if they're null (which they will be if the hash entry doesn’t exist). In that case we just return undef. Otherwise you risk a core dump again.

      Thanks. I've modified the code as above.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Re: Accessors in xs
by Roy Johnson (Monsignor) on Oct 24, 2005 at 18:27 UTC
    See if you get better results with
    sub world_location { @{$_[0]}{'x','y'} }
    Of course, one problem is that function calls are slow, and method calls are even slower. So there's some argument in favor of the admittedly inelegant and potentially dangerous route of keeping those coordinates as an accessible member array (if the API isn't already set in stone, or if you don't mind using an unadvertized interface for your critical bits).

    Also, since array access is faster than hash access, you might want to rework the implementation to use an arrayref instead of a hashref as the base structure.


    Caution: Contents may have been coded under pressure.
Re: Accessors in xs
by tilly (Archbishop) on Oct 25, 2005 at 00:38 UTC
    After converting to XS, check your performance gains.

    If they don't satisfy you, you might get another gain from converting some method calls to function calls since method lookups are significantly slower.

      Yes, I've been thinking about that. I have a few more I want to convert first. I don't like going down to function calls if the rest of the program is OO, but I might not have a choice.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        If you think your overhead is in method calls, check to see that you're not doing anything which invalidates the method cache:

        • Assigning type globs
        • Assigning functions into the symbol table
        • Creating or removing named functions
        • Modifying @ISA

        Since you're using XS already, you may want to write a short function to instrument PL_sub_generation to see if its changing. Anytime it increments, your cache is invalidated.


        New as of 5.9.4, this function is B::sub_generation

Re: Accessors in xs
by fireartist (Chaplain) on Oct 25, 2005 at 09:37 UTC

    Testing object creation and 2 method-calls, gives a 25% speedup

    use strict; use warnings; use Benchmark 'cmpthese'; use lib ('blib/lib', 'blib/arch'); use CMethods; use PerlMethods; my($x, $y); cmpthese( 200_000, { cMethods => sub { my $c = CMethods->new; $x = $c->x; $y = $c->y; }, PerlMethods => sub { my $c = PerlMethods->new; $x = $c->x; $y = $c->y; }, });
    Rate PerlMethods cMethods PerlMethods 111297/s -- -17% cMethods 134771/s 21% -- Rate PerlMethods cMethods PerlMethods 111297/s -- -20% cMethods 139179/s 25% -- Rate PerlMethods cMethods PerlMethods 108460/s -- -22% cMethods 139179/s 28% --

    Testing object creation and 200 method-calls (to lessen the overhead of creating the object), gives an impresive 78% speedup

    use strict; use warnings; use Benchmark 'cmpthese'; use lib ('blib/lib', 'blib/arch'); use CMethods; use PerlMethods; my($x, $y); cmpthese( 50_000, { cMethods => sub { my $c = CMethods->new; for (1..100) { $x = $c->x; $y = $c->y; } }, PerlMethods => sub { my $c = PerlMethods->new; for (1..100) { $x = $c->x; $y = $c->y; } }, });
    Rate PerlMethods cMethods PerlMethods 2933/s -- -44% cMethods 5220/s 78% -- Rate PerlMethods cMethods PerlMethods 2958/s -- -44% cMethods 5255/s 78% -- Rate PerlMethods cMethods PerlMethods 2949/s -- -44% cMethods 5246/s 78% --

    Here's the code I used:

      Nice. If I add some other possibilities:

      I get these results:

      Rate PerlMethods PerlSubCall cMethods cSubCall Direct + Fields PerlMethods 1875/s -- -4% -64% -72% -80% + -80% PerlSubCall 1951/s 4% -- -62% -70% -79% + -80% cMethods 5160/s 175% 164% -- -22% -44% + -46% cSubCall 6596/s 252% 238% 28% -- -29% + -31% Direct 9242/s 393% 374% 79% 40% -- + -4% Fields 9597/s 412% 392% 86% 45% 4% + --
      which (unsurprisingly) shows that, if you need a massive speedup, there's no alternative to accessing the members directly. For a final iota of speed you can use the fields mechanism: the UsingFields.pm module is:

      Update: Be warned that fields no longer gives any speedup with the development version of perl, and won't in 5.10 either.

        Nice++ It would be interesting to combine your benchmark and Re: Making an Object faster?.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Accessors in xs
by kscaldef (Pilgrim) on Oct 25, 2005 at 06:44 UTC
    I'm really curious to see benchmarks on this exercise. Honestly, I don't really see how the XS version can end up any faster. It seems that it just does exactly the same thing that the pure Perl version must do under the hood. I could be wrong, but I'd be interested to know why, if this is faster.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://502523]
Approved by Limbic~Region
Front-paged by Limbic~Region
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2023-05-31 11:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?