Skip to content

Commit 6771ea3

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 6771ea3

File tree

1 file changed

+102
-30
lines changed

1 file changed

+102
-30
lines changed

ext/mro/mro.xs

Lines changed: 102 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,79 @@ 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+
SV* ksv;
459+
assert(HeKLEN(iter) != HEf_SVKEY);
460+
ksv = newSVhek(HeKEY_hek(iter)); /* prev hv_iterkeysv(iter) */
461+
av_push_simple(ret_array, ksv);
462+
}
402463
}
403-
mXPUSHs(newRV_noinc(MUTABLE_SV(ret_array)));
404-
405-
PUTBACK;
464+
mPUSHs(newRV_noinc(MUTABLE_SV(ret_array)));
406465

407466
void
408467
mro_is_universal(...)
409468
PROTOTYPE: $
410469
PREINIT:
411470
SV* classname;
412471
HV* isarev;
413-
char* classname_pv;
414-
STRLEN classname_len;
415472
HE* he;
473+
SV* rsv;
416474
PPCODE:
417475
if (items != 1)
418476
croak_xs_usage(cv, "classname");
419477

420478
classname = ST(0);
421479

422-
classname_pv = SvPV(classname,classname_len);
423-
424480
he = hv_fetch_ent(PL_isarev, classname, 0, 0);
425481
isarev = he ? MUTABLE_HV(HeVAL(he)) : NULL;
426482

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

433500

434501
void
435502
mro_invalidate_all_method_caches(...)
436503
PROTOTYPE:
437504
PPCODE:
505+
SP = MARK;
506+
PUTBACK;
438507
if (items != 0)
439508
croak_xs_usage(cv, "");
440-
441509
PL_sub_generation++;
442-
443-
XSRETURN_EMPTY;
510+
return;
444511

445512
void
446513
mro_get_pkg_gen(...)
447514
PROTOTYPE: $
448515
PREINIT:
449516
SV* classname;
450517
HV* class_stash;
518+
IV RETVAL;
519+
dXSTARG; /* CODE: + IV retval + prototypes seems to be broken in EU::PXS */
451520
PPCODE:
452521
if(items != 1)
453522
croak_xs_usage(cv, "classname");
454-
455523
classname = ST(0);
456-
457-
class_stash = gv_stashsv(classname, 0);
458-
459-
mXPUSHi(class_stash ? HvMROMETA(class_stash)->pkg_gen : 0);
460-
524+
PUSHs(TARG);
461525
PUTBACK;
526+
class_stash = gv_stashsv(classname, 0);
527+
RETVAL = class_stash ? HvMROMETA(class_stash)->pkg_gen : 0;
528+
TARGi(RETVAL,1);
529+
return;
462530

463531
void
464532
mro__nextcan(...)
@@ -676,3 +744,7 @@ BOOT:
676744
}
677745
}
678746
Perl_mro_register(aTHX_ &c3_alg);
747+
{
748+
MY_CXT_INIT;
749+
init_MY_CXT(aTHX_ aMY_CXT);
750+
}

0 commit comments

Comments
 (0)