Skip to content

Commit 5f6c822

Browse files
committed
mro.xs @_ stack cleanup, EXTEND rmvs, more HEK* COWs, U32 hash precalcs
-HEK* cache str "UNIVERSAL" its heavily used inside the Perl VM, and take advantage of shared HEK* to HEK* comparison loop optimization inside hv_common -HEK* cache str "dfs", it is ineligible for COW 255 since its under 9 chars long -mro_get_mro() cleanup retval, PPCODE: moves SP back to 0, we push 1 elem then EUPXS does a PUTBACK for us, this removes alot of PL_stack_base derefs - av_push_simple(ret_array, newSVsv(hv_iterkeysv(iter))); is created 2 SV*s for no good reason, someone earlier never read the api docs for what hv_iterkeysv() does, or looked at its internals -mXPUSHs -> mPUSHs, X not needed, we bounds checked if (items != 1) and rewind with PPCODE earlier -switch to SvPV_const(), COW future proofing -hv_existss(isarev, "UNIVERSAL") switch to SVPV HEK* for faster lookups and precalced U32 hash -mro_get_pkg_gen() just use EU::PXS's built in IV retval logic instead of DIYing it, newer EU::PXSs use dXSTARG; and TARGi,(); optimization -mro_invalidate_all_method_caches() do @_ logic as early as possible croak_xs_usage() doesn't care if MARK was popped, or how much forwards or backwards the SV** inside PL_stack_sp is. Doing this allows the smallest machine code possible on any CPU arch, since the offset literals into my_perl, or unthreaded offsets into libperl.so.dll, that do the reads and writes are very close together (RISC CPUs might need a dedicated load const litteral integer to register op, to reach around inside my_perl or libperl.so.dll, they can't put a variable length 1-4 byte opcode + (U16 || U32) litteral into 1 RISC op. ARM's limit is U12 for example.
1 parent 8d32ca0 commit 5f6c822

File tree

1 file changed

+101
-30
lines changed

1 file changed

+101
-30
lines changed

ext/mro/mro.xs

Lines changed: 101 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,27 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level);
1010
static struct mro_alg c3_alg =
1111
{S_mro_get_linear_isa_c3, "c3", 2, 0, 0};
1212

13+
#define MY_CXT_KEY "mro::_guts"
14+
15+
typedef struct {
16+
SV *sv_UNIVERSAL;
17+
SV *sv_dfs;
18+
SV *sv_ISA;
19+
} my_cxt_t;
20+
21+
START_MY_CXT
22+
23+
static void
24+
init_MY_CXT(pTHX_ pMY_CXT)
25+
{
26+
MY_CXT.sv_UNIVERSAL = newSVpvs_share("UNIVERSAL");
27+
SvREADONLY_on(MY_CXT.sv_UNIVERSAL);
28+
MY_CXT.sv_dfs = newSVpvs_share("dfs");
29+
SvREADONLY_on(MY_CXT.sv_dfs);
30+
MY_CXT.sv_ISA = newSVpvs_share("ISA");
31+
SvREADONLY_on(MY_CXT.sv_ISA);
32+
}
33+
1334
/*
1435
=for apidoc mro_get_linear_isa_c3
1536
@@ -32,7 +53,6 @@ static AV*
3253
S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
3354
{
3455
AV* retval;
35-
GV** gvp;
3656
GV* gv;
3757
AV* isa;
3858
const HEK* stashhek;
@@ -58,9 +78,9 @@ S_mro_get_linear_isa_c3(pTHX_ HV* stash, U32 level)
5878
}
5979

6080
/* not in cache, make a new one */
61-
62-
gvp = (GV**)hv_fetchs(stash, "ISA", FALSE);
63-
isa = (gvp && (gv = *gvp) && isGV_with_GP(gv)) ? GvAV(gv) : NULL;
81+
dMY_CXT;
82+
HE* he = hv_fetch_ent(stash, MY_CXT.sv_ISA, FALSE, 0);
83+
isa = (he && (gv = (GV*)HeVAL(he)) && isGV_with_GP(gv)) ? GvAV(gv) : NULL;
6484

6585
/* For a better idea how the rest of this works, see the much clearer
6686
pure perl version in Algorithm::C3 0.01:
@@ -299,6 +319,41 @@ __dopoptosub_at(const PERL_CONTEXT *cxstk, I32 startingblock) {
299319

300320
MODULE = mro PACKAGE = mro PREFIX = mro_
301321

322+
#ifdef PERL_IMPLICIT_CONTEXT
323+
324+
void CLONE (...)
325+
CODE:
326+
#undef memcpy
327+
#define memcpy(a,b,c) NOOP
328+
{
329+
MY_CXT_CLONE; /* possible declaration */
330+
init_MY_CXT(aTHX_ aMY_CXT);
331+
}
332+
#undef memcpy
333+
/* skip implicit PUTBACK, returning @_ to caller, more efficient */
334+
return;
335+
336+
#endif
337+
338+
void END(...)
339+
PREINIT:
340+
SV * sv;
341+
PPCODE:
342+
if (PL_perl_destruct_level) {
343+
dMY_CXT;
344+
sv = MY_CXT.sv_UNIVERSAL;
345+
MY_CXT.sv_UNIVERSAL = NULL;
346+
SvREFCNT_dec_NN(sv);
347+
sv = MY_CXT.sv_dfs;
348+
MY_CXT.sv_dfs = NULL;
349+
SvREFCNT_dec_NN(sv);
350+
sv = MY_CXT.sv_ISA;
351+
MY_CXT.sv_ISA = NULL;
352+
SvREFCNT_dec_NN(sv);
353+
}
354+
/* skip implicit PUTBACK, returning @_ to caller, more efficient*/
355+
return;
356+
302357
void
303358
mro_get_linear_isa(...)
304359
PROTOTYPE: $;$
@@ -359,6 +414,7 @@ mro_get_mro(...)
359414
PREINIT:
360415
SV* classname;
361416
HV* class_stash;
417+
SV* retsv;
362418
PPCODE:
363419
if (items != 1)
364420
croak_xs_usage(cv, "classname");
@@ -368,13 +424,14 @@ mro_get_mro(...)
368424

369425
if (class_stash) {
370426
const struct mro_alg *const meta = HvMROMETA(class_stash)->mro_which;
371-
ST(0) = newSVpvn_flags(meta->name, meta->length,
427+
retsv = newSVpvn_flags(meta->name, meta->length,
372428
SVs_TEMP
373429
| ((meta->kflags & HVhek_UTF8) ? SVf_UTF8 : 0));
374430
} else {
375-
ST(0) = newSVpvn_flags("dfs", 3, SVs_TEMP);
431+
dMY_CXT;
432+
retsv = newSVhek_mortal(SvSHARED_HEK_FROM_PV(SvPVX(MY_CXT.sv_dfs)));
376433
}
377-
XSRETURN(1);
434+
PUSHs(retsv);
378435

379436
void
380437
mro_get_isarev(...)
@@ -397,68 +454,78 @@ mro_get_isarev(...)
397454
if(isarev) {
398455
HE* iter;
399456
hv_iterinit(isarev);
400-
while((iter = hv_iternext(isarev)))
401-
av_push_simple(ret_array, newSVsv(hv_iterkeysv(iter)));
457+
while((iter = hv_iternext(isarev))) {
458+
assert(HeKLEN(iter) != HEf_SVKEY);
459+
SV* ksv = newSVhek(HeKEY_hek(iter)); /* prev hv_iterkeysv(iter) */
460+
av_push_simple(ret_array, ksv);
461+
}
402462
}
403-
mXPUSHs(newRV_noinc(MUTABLE_SV(ret_array)));
404-
405-
PUTBACK;
463+
mPUSHs(newRV_noinc(MUTABLE_SV(ret_array)));
406464

407465
void
408466
mro_is_universal(...)
409467
PROTOTYPE: $
410468
PREINIT:
411469
SV* classname;
412470
HV* isarev;
413-
char* classname_pv;
414-
STRLEN classname_len;
415471
HE* he;
472+
SV* rsv;
416473
PPCODE:
417474
if (items != 1)
418475
croak_xs_usage(cv, "classname");
419476

420477
classname = ST(0);
421478

422-
classname_pv = SvPV(classname,classname_len);
423-
424479
he = hv_fetch_ent(PL_isarev, classname, 0, 0);
425480
isarev = he ? MUTABLE_HV(HeVAL(he)) : NULL;
426481

427-
if((memEQs(classname_pv, classname_len, "UNIVERSAL"))
428-
|| (isarev && hv_existss(isarev, "UNIVERSAL")))
429-
XSRETURN_YES;
430-
else
431-
XSRETURN_NO;
482+
STRLEN classname_len;
483+
const char* classname_pv = SvPV_const(classname, classname_len);
484+
if(memEQs(classname_pv, classname_len, "UNIVERSAL"))
485+
rsv = &PL_sv_yes;
486+
else {
487+
if (isarev) {
488+
dMY_CXT;
489+
if (hv_exists_ent(isarev, MY_CXT.sv_UNIVERSAL, 0))
490+
rsv = &PL_sv_yes;
491+
else
492+
rsv = &PL_sv_no;
493+
}
494+
else
495+
rsv = &PL_sv_no;
496+
}
497+
PUSHs(rsv);
432498

433499

434500
void
435501
mro_invalidate_all_method_caches(...)
436502
PROTOTYPE:
437503
PPCODE:
504+
SP = MARK;
505+
PUTBACK;
438506
if (items != 0)
439507
croak_xs_usage(cv, "");
440-
441508
PL_sub_generation++;
442-
443-
XSRETURN_EMPTY;
509+
return;
444510

445511
void
446512
mro_get_pkg_gen(...)
447513
PROTOTYPE: $
448514
PREINIT:
449515
SV* classname;
450516
HV* class_stash;
517+
IV RETVAL;
518+
dXSTARG; /* CODE: + IV retval + prototypes seems to be broken in EU::PXS */
451519
PPCODE:
452520
if(items != 1)
453521
croak_xs_usage(cv, "classname");
454-
455522
classname = ST(0);
456-
457-
class_stash = gv_stashsv(classname, 0);
458-
459-
mXPUSHi(class_stash ? HvMROMETA(class_stash)->pkg_gen : 0);
460-
523+
PUSHs(TARG);
461524
PUTBACK;
525+
class_stash = gv_stashsv(classname, 0);
526+
RETVAL = class_stash ? HvMROMETA(class_stash)->pkg_gen : 0;
527+
TARGi(RETVAL,1);
528+
return;
462529

463530
void
464531
mro__nextcan(...)
@@ -676,3 +743,7 @@ BOOT:
676743
}
677744
}
678745
Perl_mro_register(aTHX_ &c3_alg);
746+
{
747+
MY_CXT_INIT;
748+
init_MY_CXT(aTHX_ aMY_CXT);
749+
}

0 commit comments

Comments
 (0)