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.
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.
| [reply] [d/l] |
|
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.
| [reply] [d/l] |
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?
| [reply] [d/l] |
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:
- 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.
- 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.
| [reply] [d/l] |
|
| [reply] |
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.
| [reply] [d/l] |
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. | [reply] |
|
| [reply] |
|
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
| [reply] |
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:
| [reply] [d/l] [select] |
|
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. | [reply] [d/l] [select] |
|
| [reply] |
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. | [reply] |
|
|