Change 34354 by [EMAIL PROTECTED] on 2008/09/12 00:19:51
Create a direct lookup hash for ->isa() lookup, by retaining the
de-duping hash used by S_mro_get_linear_isa_dfs(). Provide a new
function Perl_get_isa_hash() to lazily retrieve this. (Which could
actually be static if S_isa_lookup() and Perl_sv_derived_from()
moved into mro.c.) Make S_isa_lookup() use this lookup hash in place
of a linear walk of the linear isa. This should turn isa lookups from
O(n) to O(1), which should make heavy users of ->isa() faster.
(eg PPI, and hence Perl Critic).
Affected files ...
... //depot/perl/embed.fnc#618 edit
... //depot/perl/hv.c#379 edit
... //depot/perl/hv.h#124 edit
... //depot/perl/mro.c#50 edit
... //depot/perl/proto.h#952 edit
... //depot/perl/universal.c#199 edit
Differences ...
==== //depot/perl/embed.fnc#618 (text) ====
Index: perl/embed.fnc
--- perl/embed.fnc#617~34351~ 2008-09-11 14:27:43.000000000 -0700
+++ perl/embed.fnc 2008-09-11 17:19:51.000000000 -0700
@@ -1997,6 +1997,7 @@
|NULLOK STRLEN *len|NULLOK U32 *flags
xpoM |struct refcounted_he *|store_cop_label \
|NULLOK struct refcounted_he *const chain|NN const char *label
+poM |HV * |get_isa_hash |NN HV *const stash
END_EXTERN_C
/*
==== //depot/perl/hv.c#379 (text) ====
Index: perl/hv.c
--- perl/hv.c#378~34338~ 2008-09-11 00:53:36.000000000 -0700
+++ perl/hv.c 2008-09-11 17:19:51.000000000 -0700
@@ -1688,6 +1688,7 @@
if(meta->mro_linear_dfs) SvREFCNT_dec(meta->mro_linear_dfs);
if(meta->mro_linear_c3) SvREFCNT_dec(meta->mro_linear_c3);
if(meta->mro_nextmethod) SvREFCNT_dec(meta->mro_nextmethod);
+ SvREFCNT_dec(meta->isa);
Safefree(meta);
iter->xhv_mro_meta = NULL;
}
==== //depot/perl/hv.h#124 (text) ====
Index: perl/hv.h
--- perl/hv.h#123~33891~ 2008-05-20 11:37:03.000000000 -0700
+++ perl/hv.h 2008-09-11 17:19:51.000000000 -0700
@@ -52,6 +52,7 @@
U32 cache_gen; /* Bumping this invalidates our method cache */
U32 pkg_gen; /* Bumps when local methods/@ISA change */
const struct mro_alg *mro_which; /* which mro alg is in use? */
+ HV *isa; /* Everything this class @ISA */
};
/* Subject to change.
==== //depot/perl/mro.c#50 (text) ====
Index: perl/mro.c
--- perl/mro.c#49~34215~ 2008-08-22 00:00:17.000000000 -0700
+++ perl/mro.c 2008-09-11 17:19:51.000000000 -0700
@@ -88,12 +88,29 @@
if (newmeta->mro_nextmethod)
newmeta->mro_nextmethod
= (HV*) SvREFCNT_inc(sv_dup((SV*)newmeta->mro_nextmethod, param));
+ if (newmeta->isa)
+ newmeta->isa
+ = (HV*) SvREFCNT_inc(sv_dup((SV*)newmeta->isa, param));
return newmeta;
}
#endif /* USE_ITHREADS */
+HV *
+Perl_get_isa_hash(pTHX_ HV *const stash)
+{
+ dVAR;
+ struct mro_meta *const meta = HvMROMETA(stash);
+
+ PERL_ARGS_ASSERT_GET_ISA_HASH;
+
+ if (!meta->isa)
+ mro_get_linear_isa_dfs(stash, 0);
+ assert(meta->isa);
+ return meta->isa;
+}
+
/*
=for apidoc mro_get_linear_isa_dfs
@@ -119,6 +136,8 @@
AV* av;
const HEK* stashhek;
struct mro_meta* meta;
+ SV *our_name;
+ HV *stored;
PERL_ARGS_ASSERT_MRO_GET_LINEAR_ISA_DFS;
assert(HvAUX(stash));
@@ -141,20 +160,25 @@
/* not in cache, make a new one */
retval = (AV*)sv_2mortal((SV *)newAV());
- av_push(retval, newSVhek(stashhek)); /* add ourselves at the top */
+ /* We use this later in this function, but don't need a reference to it
+ beyond the end of this function, so reference count is fine. */
+ our_name = newSVhek(stashhek);
+ av_push(retval, our_name); /* add ourselves at the top */
/* fetch our @ISA */
gvp = (GV**)hv_fetchs(stash, "ISA", FALSE);
av = (gvp && (gv = *gvp) && isGV_with_GP(gv)) ? GvAV(gv) : NULL;
- if(av && AvFILLp(av) >= 0) {
+ /* "stored" is used to keep track of all of the classnames we have added to
+ the MRO so far, so we can do a quick exists check and avoid adding
+ duplicate classnames to the MRO as we go.
+ It's then retained to be re-used as a fast lookup for ->isa(), by adding
+ our own name and "UNIVERSAL" to it. */
- /* "stored" is used to keep track of all of the classnames
- we have added to the MRO so far, so we can do a quick
- exists check and avoid adding duplicate classnames to
- the MRO as we go. */
+ stored = (HV*)sv_2mortal((SV*)newHV());
+
+ if(av && AvFILLp(av) >= 0) {
- HV* const stored = (HV*)sv_2mortal((SV*)newHV());
SV **svp = AvARRAY(av);
I32 items = AvFILLp(av) + 1;
@@ -221,12 +245,19 @@
mortals' stack will be released soon, so everything will balance. */
SvREFCNT_inc_simple_void_NN(retval);
SvTEMP_off(retval);
+ SvREFCNT_inc_simple_void_NN(stored);
+ SvTEMP_off(stored);
+
+ hv_store_ent(stored, our_name, &PL_sv_undef, 0);
+ hv_store(stored, "UNIVERSAL", 9, &PL_sv_undef, 0);
/* we don't want anyone modifying the cache entry but us,
and we do so by replacing it completely */
SvREADONLY_on(retval);
+ SvREADONLY_on(stored);
meta->mro_linear_dfs = retval;
+ meta->isa = stored;
return retval;
}
==== //depot/perl/proto.h#952 (text+w) ====
Index: perl/proto.h
--- perl/proto.h#951~34351~ 2008-09-11 14:27:43.000000000 -0700
+++ perl/proto.h 2008-09-11 17:19:51.000000000 -0700
@@ -6597,6 +6597,11 @@
#define PERL_ARGS_ASSERT_STORE_COP_LABEL \
assert(label)
+PERL_CALLCONV HV * Perl_get_isa_hash(pTHX_ HV *const stash)
+ __attribute__nonnull__(pTHX_1);
+#define PERL_ARGS_ASSERT_GET_ISA_HASH \
+ assert(stash)
+
END_EXTERN_C
/*
==== //depot/perl/universal.c#199 (text) ====
Index: perl/universal.c
--- perl/universal.c#198~34353~ 2008-09-11 15:11:32.000000000 -0700
+++ perl/universal.c 2008-09-11 17:19:51.000000000 -0700
@@ -40,35 +40,32 @@
S_isa_lookup(pTHX_ HV *stash, const char * const name)
{
dVAR;
- AV* stash_linear_isa;
- SV** svp;
- const char *hvname;
- I32 items;
- const HV *const name_stash = gv_stashpv(name, 0);
+ const struct mro_meta *const meta = HvMROMETA(stash);
+ HV *const isa = meta->isa ? meta->isa : Perl_get_isa_hash(aTHX_ stash);
+ STRLEN len = strlen(name);
+ const HV *our_stash;
PERL_ARGS_ASSERT_ISA_LOOKUP;
- /* A stash/class can go by many names (ie. User == main::User), so
- we compare the stash itself just in case */
- if ((const HV *)stash == name_stash)
- return TRUE;
-
- hvname = HvNAME_get(stash);
-
- if (strEQ(hvname, name))
+ if (hv_common(isa, NULL, name, len, 0 /* No "UTF-8" flag possible with only
+ a char * argument*/,
+ HV_FETCH_ISEXISTS, NULL, 0)) {
+ /* Direct name lookup worked. */
return TRUE;
+ }
- if (strEQ(name, "UNIVERSAL"))
- return TRUE;
+ /* A stash/class can go by many names (ie. User == main::User), so
+ we use the name in the stash itself, which is canonical. */
+ our_stash = gv_stashpvn(name, len, 0);
- stash_linear_isa = mro_get_linear_isa(stash);
- svp = AvARRAY(stash_linear_isa) + 1;
- items = AvFILLp(stash_linear_isa);
- while (items--) {
- SV* const basename_sv = *svp++;
- HV* const basestash = gv_stashsv(basename_sv, 0);
- if(name_stash == basestash || strEQ(name, SvPVX(basename_sv)))
+ if (our_stash) {
+ HEK *const canon_name = HvNAME_HEK(our_stash);
+
+ if (hv_common(isa, NULL, HEK_KEY(canon_name), HEK_LEN(canon_name),
+ HEK_FLAGS(canon_name),
+ HV_FETCH_ISEXISTS, NULL, HEK_HASH(canon_name))) {
return TRUE;
+ }
}
return FALSE;
End of Patch.