patcat88 has asked for the wisdom of the Perl Monks concerning the following question:
I've been thinking of how to speed up XS code, and saw many hv functions take a "hash number". Its 99% of the time zero in every example I've seen. Your supposed to use the "PERL_HASH" macro to calculate the hash number. The only example I have found of PERL_HASH being used anywhere was here http://cpansearch.perl.org/src/DOY/Moose-1.9902-TRIAL/mop.c
I've been having problems figuring out if its faster or SLOWER to use precalculated hashes. I wrote a test library to time the difference. The Time::HiRes time() benchmarking says caching the key hashes is faster (get was 0.95s, getKC was 0.89s). My C profiler (DevPartner 8) says no key caching is faster (get=5.81us per run, getKC=5.89us per run). Nytprof says no key caching is faster (get is 516ms, getKC is 531ms).
I did my tests on Windows XP ActivePerl 5.10 build 1003 compiled with VS2003.
here is XHVKC.xs
here is XHVKC.pm
here is the makefile.pl, it may or may not apply to you, I've disabled a few compiler optimizations to enable step through debugging in VS
the test script
I've been having problems figuring out if its faster or SLOWER to use precalculated hashes. I wrote a test library to time the difference. The Time::HiRes time() benchmarking says caching the key hashes is faster (get was 0.95s, getKC was 0.89s). My C profiler (DevPartner 8) says no key caching is faster (get=5.81us per run, getKC=5.89us per run). Nytprof says no key caching is faster (get is 516ms, getKC is 531ms).
I did my tests on Windows XP ActivePerl 5.10 build 1003 compiled with VS2003.
here is XHVKC.xs
#define PERL_NO_GET_CONTEXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" typedef struct { const char * key; unsigned long hash; const signed long klen; } hvCacheKeyStruct; typedef struct { hvCacheKeyStruct jiugsdh1; hvCacheKeyStruct iusidfsd2; hvCacheKeyStruct ihfsdgsfg3; hvCacheKeyStruct sudfyf4; hvCacheKeyStruct sfyuihldfss5; hvCacheKeyStruct iuodafohsd6; hvCacheKeyStruct kjsdjdfsj7; hvCacheKeyStruct layer1; hvCacheKeyStruct layer2; hvCacheKeyStruct layer3; } hvCacheStruct; #define HVCKSINIT(keyname) {#keyname, 0, sizeof(#keyname)-1 } static hvCacheStruct hvCache = { HVCKSINIT(jiugsdh1), HVCKSINIT(iusidfsd2), HVCKSINIT(ihfsdgsfg3), HVCKSINIT(sudfyf4), HVCKSINIT(sfyuihldfss5), HVCKSINIT(iuodafohsd6), HVCKSINIT(kjsdjdfsj7), HVCKSINIT(layer1), HVCKSINIT(layer2), HVCKSINIT(layer3) }; #undef HVCKSINIT #define HVKC_KEY(x) (hvCache.x.key) #define HVKC_KLEN(x) (hvCache.x.klen) #define HVKC_HASH(x) (hvCache.x.hash) #define HVKC_ARR_KEY(x) ((((hvCacheKeyStruct*)&(hvCache))+(x))->key) #define HVKC_ARR_KLEN(x) ((((hvCacheKeyStruct*)&(hvCache))+(x))->klen) #define HVKC_ARR_HASH(x) ((((hvCacheKeyStruct*)&(hvCache))+(x))->hash) #define hvkc_hv_fetch(hv, key, klen, lval, hash) + \ ((SV**) hv_common_key_len((hv), (key), (klen), (lval) \ ? (HV_FETCH_JUST_SV | HV_FETCH_LVALUE) \ : HV_FETCH_JUST_SV, NULL, (hash))) #define hvkc_hv_fetch_kc(hv, key, lval) + \ ((SV**) hv_common_key_len((hv), (HVKC_KEY(key)), (HVKC_KLEN(key)), (lv +al) \ ? (HV_FETCH_JUST_SV | HV_FETCH_LVALUE) \ : HV_FETCH_JUST_SV, NULL, (HVKC_HASH(key)))) void initializeHVCache(pTHX) { unsigned long i; const unsigned long arraylength = sizeof(hvCacheStruct)/sizeof(hvC +acheKeyStruct); for(i=0; i<arraylength; i++){ printf("initializeHVCache b4 %s hash=%u klen=%u\n", (((hvCacheKeyStruct*)&(hvCache))+i)->key, (((hvCacheKeyStruct*)&(hvCache))+i)->hash, (((hvCacheKeyStruct*)&(hvCache))+i)->klen ); PERL_HASH( ((((hvCacheKeyStruct*)&(hvCache))+i)->hash), ((((hvCacheKeyStruct*)&(hvCache))+i)->key), ((((hvCacheKeyStruct*)&(hvCache))+i)->klen) ); printf("initializeHVCache after %s hash=%u klen=%u\n", (((hvCacheKeyStruct*)&(hvCache))+i)->key, (((hvCacheKeyStruct*)&(hvCache))+i)->hash, (((hvCacheKeyStruct*)&(hvCache))+i)->klen ); } } MODULE = XHVKC PACKAGE = XHVKC BOOT: initializeHVCache(aTHX); void get(self, num) SV * self unsigned long num PREINIT: void * voidPtr = 0; char * finalKeyName; unsigned long finalKeyNameLen; CODE: if (SvROK((HV*)self) && SvTYPE(SvRV((HV*)self))==SVt_PVHV) self = SvRV((HV*)self); else Perl_croak(aTHX_ "%s: %s is not a hash reference", "XHVKC::get", "self"); #define SWITCHMACRO(num,key) case (num): finalKeyName = #key; finalKey +NameLen = sizeof(#key)-1; break; switch(num){ SWITCHMACRO(1,jiugsdh1) SWITCHMACRO(2,iusidfsd2) SWITCHMACRO(3,ihfsdgsfg3) SWITCHMACRO(4,sudfyf4) SWITCHMACRO(5,sfyuihldfss5) SWITCHMACRO(6,iuodafohsd6) SWITCHMACRO(7,kjsdjdfsj7) #undef SWITCHMACRO default: printf("huh?"); exit(1); } (SV **)voidPtr = hvkc_hv_fetch((HV *)self, "layer1", sizeof("layer +1")-1, 0,0); if(voidPtr){ (SV *)voidPtr = *(SV **)voidPtr; if (SvROK((SV *)voidPtr)){ (SV *)voidPtr = SvRV((SV *)voidPtr); if (SvTYPE((SV *)voidPtr)==SVt_PVHV) { (SV **)voidPtr = hvkc_hv_fetch((HV *)voidPtr, "layer2" +, sizeof("layer2")-1,0,0); if(voidPtr) { (SV *)voidPtr = *(SV **)voidPtr; if (SvROK((SV *)voidPtr)){ (SV *)voidPtr = SvRV((SV *)voidPtr); if (SvTYPE((SV *)voidPtr)==SVt_PVHV) { (SV **)voidPtr = hvkc_hv_fetch((HV *)voidP +tr, "layer3", sizeof("layer3")-1,0,0); if(voidPtr) { (SV *)voidPtr = *(SV **)voidPtr; if (SvROK((SV *)voidPtr)){ (SV *)voidPtr = SvRV((SV *)voidPtr +); if (SvTYPE((SV *)voidPtr)==SVt_PVH +V) { (SV **)voidPtr = hvkc_hv_fetch +((HV *)voidPtr, finalKeyName, finalKeyNameLen,0,0); if(voidPtr) { voidPtr = (void *)SvIV(*(S +V **)voidPtr); } //else stays null } else {voidPtr = NULL;} } else {voidPtr = NULL;} } //else stays null } else {voidPtr = NULL;} } else {voidPtr = NULL;} } //else stays null } else {voidPtr = NULL;} } else {voidPtr = NULL;} } //else stays null if(voidPtr != (void *)999) { printf("wrong number at end of hash tree"); exit(1); } void getKC(self, num) SV * self unsigned long num PREINIT: void * voidPtr = 0; CODE: if (SvROK((HV*)self) && SvTYPE(SvRV((HV*)self))==SVt_PVHV) self = SvRV((HV*)self); else Perl_croak(aTHX_ "%s: %s is not a hash reference", "XHVKC::get", "self"); (SV **)voidPtr = hvkc_hv_fetch_kc((HV *)self, layer1,0); if(voidPtr){ (SV *)voidPtr = *(SV **)voidPtr; if (SvROK((SV *)voidPtr)){ (SV *)voidPtr = SvRV((SV *)voidPtr); if (SvTYPE((SV *)voidPtr)==SVt_PVHV) { (SV **)voidPtr = hvkc_hv_fetch_kc((HV *)voidPtr, layer +2,0); if(voidPtr) { (SV *)voidPtr = *(SV **)voidPtr; if (SvROK((SV *)voidPtr)){ (SV *)voidPtr = SvRV((SV *)voidPtr); if (SvTYPE((SV *)voidPtr)==SVt_PVHV) { (SV **)voidPtr = hvkc_hv_fetch_kc((HV *)vo +idPtr, layer3,0); if(voidPtr) { (SV *)voidPtr = *(SV **)voidPtr; if (SvROK((SV *)voidPtr)){ (SV *)voidPtr = SvRV((SV *)voidPtr +); if (SvTYPE((SV *)voidPtr)==SVt_PVH +V) { num -= 1; (SV **)voidPtr = hvkc_hv_fetch +((HV *)voidPtr, HVKC_ARR_KEY(num), HVKC_ARR_KLEN(num), 0, HVKC_ARR_HA +SH(num)); if(voidPtr) { voidPtr = (void *)SvIV(*(S +V **)voidPtr); } //else stays null } else {voidPtr = NULL;} } else {voidPtr = NULL;} } //else stays null } else {voidPtr = NULL;} } else {voidPtr = NULL;} } //else stays null } else {voidPtr = NULL;} } else {voidPtr = NULL;} } //else stays null if(voidPtr != (void *)999) { printf("wrong number at end of hash tree"); exit(1); }
package XHVKC; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); # Items to export into callers namespace by default. Note: do not expo +rt # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use XHVKC ':all'; # If you do not need this, moving things directly into @EXPORT or @EXP +ORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.01'; require XSLoader; XSLoader::load('XHVKC', $VERSION); sub new { my $href = { 'layer1' => { 'layer2' => { 'layer3' => { 'jiugsdh1' => 999, 'iusidfsd2' => 999, 'ihfsdgsfg3' => 999, 'sudfyf4' => 999, 'sfyuihldfss5' => 999, 'iuodafohsd6' => 999, 'kjsdjdfsj7' => 999 } } } }; bless $href ; return $href ; } # Preloaded methods go here. 1; __END__
use 5.010000; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. use Config qw(%Config); my $ccflags = $Config{'ccflags'}; warn $ccflags."\n"; $ccflags =~ s/O1/Od/; $ccflags =~ s/-DNDEBUG//; warn $ccflags."\n"; WriteMakefile( NAME => 'XHVKC', VERSION_FROM => 'lib/XHVKC.pm', # finds $VERSION PREREQ_PM => {}, # e.g., Module::Name => 1.1 ($] >= 5.005 ? ## Add these new keywords supported since 5.005 (ABSTRACT_FROM => 'lib/XHVKC.pm', # retrieve abstract from modu +le AUTHOR => 'A. U. Thor <a.u.thor@a.galaxy.far.far.away>' +) : ()), LIBS => [''], # e.g., '-lm' DEFINE => '', # e.g., '-DHAVE_SOMETHING' INC => '-I.', # e.g., '-I. -I/usr/include/other' CCFLAGS => $ccflags, OPTIMIZE => ' ', XSOPT => ' -nolinenumbers ' # Un-comment this if you add C files to link with later: # OBJECT => '$(O_FILES)', # link all the C files too );
#!/usr/bin/perl -w use XHVKC; use Time::HiRes qw( time ); $o = new XHVKC; $time = time; for(0..300000){ for (int(rand 6)+1) {$o->get($_);} } print "time was ".(time-$time)."\n"; $time = time; for(0..300000){ for (int(rand 6)+1) {$o->getKC($_);} } print "time was ".(time-$time)."\n";
Back to
Seekers of Perl Wisdom