From: Nicholas Clark Date: Sun, 24 Jan 2010 15:07:50 +0000 (+0000) Subject: Make HvFILL() count the allocated buckets, instead of reading a stored value. X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=4d0fbddde6c5dcb972786d09de0cab6e93056b88;p=p5sagit%2Fp5-mst-13.2.git Make HvFILL() count the allocated buckets, instead of reading a stored value. Add a function Perl_hv_fill to perform the count. This will save 1 IV per hash, and on some systems cause struct xpvhv to become cache aligned. --- diff --git a/embed.fnc b/embed.fnc index 00629fdb..1f2ecb9 100644 --- a/embed.fnc +++ b/embed.fnc @@ -461,6 +461,7 @@ Ap |void* |hv_common |NULLOK HV *hv|NULLOK SV *keysv \ Ap |void* |hv_common_key_len|NULLOK HV *hv|NN const char *key \ |I32 klen_i32|const int action|NULLOK SV *val \ |const U32 hash +Apod |STRLEN |hv_fill |NN HV const *const hv Ap |void |hv_free_ent |NN HV *hv|NULLOK HE *entryK Apd |I32 |hv_iterinit |NN HV *hv ApdR |char* |hv_iterkey |NN HE* entry|NN I32* retlen diff --git a/global.sym b/global.sym index 5cbfe3f..6cffeb8 100644 --- a/global.sym +++ b/global.sym @@ -167,6 +167,7 @@ Perl_hv_fetch Perl_hv_fetch_ent Perl_hv_common Perl_hv_common_key_len +Perl_hv_fill Perl_hv_free_ent Perl_hv_iterinit Perl_hv_iterkey diff --git a/hv.c b/hv.c index c7ca826..244feb6 100644 --- a/hv.c +++ b/hv.c @@ -817,7 +817,6 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen, xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */ if (!counter) { /* initial entry? */ - xhv->xhv_fill++; /* HvFILL(hv)++ */ } else if (xhv->xhv_keys > (IV)xhv->xhv_max) { hsplit(hv); } else if(!HvREHASH(hv)) { @@ -1055,9 +1054,6 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen, HvPLACEHOLDERS(hv)++; } else { *oentry = HeNEXT(entry); - if(!*first_entry) { - xhv->xhv_fill--; /* HvFILL(hv)-- */ - } if (SvOOK(hv) && entry == HvAUX(hv)->xhv_eiter /* HvEITER(hv) */) HvLAZYDEL_on(hv); else @@ -1156,8 +1152,6 @@ S_hsplit(pTHX_ HV *hv) if ((HeHASH(entry) & newsize) != (U32)i) { *oentry = HeNEXT(entry); HeNEXT(entry) = *bep; - if (!*bep) - xhv->xhv_fill++; /* HvFILL(hv)++ */ *bep = entry; right_length++; continue; @@ -1167,8 +1161,6 @@ S_hsplit(pTHX_ HV *hv) left_length++; } } - if (!*aep) /* everything moved */ - xhv->xhv_fill--; /* HvFILL(hv)-- */ /* I think we don't actually need to keep track of the longest length, merely flag if anything is too long. But for the moment while developing this code I'll track it. */ @@ -1204,7 +1196,6 @@ S_hsplit(pTHX_ HV *hv) was_shared = HvSHAREKEYS(hv); - xhv->xhv_fill = 0; HvSHAREKEYS_off(hv); HvREHASH_on(hv); @@ -1239,8 +1230,6 @@ S_hsplit(pTHX_ HV *hv) /* Copy oentry to the correct new chain. */ bep = ((HE**)a) + (hash & (I32) xhv->xhv_max); - if (!*bep) - xhv->xhv_fill++; /* HvFILL(hv)++ */ HeNEXT(entry) = *bep; *bep = entry; @@ -1330,16 +1319,13 @@ Perl_hv_ksplit(pTHX_ HV *hv, IV newmax) if (j != i) { j -= i; *oentry = HeNEXT(entry); - if (!(HeNEXT(entry) = aep[j])) - xhv->xhv_fill++; /* HvFILL(hv)++ */ + HeNEXT(entry) = aep[j]; aep[j] = entry; continue; } else oentry = &HeNEXT(entry); } - if (!*aep) /* everything moved */ - xhv->xhv_fill--; /* HvFILL(hv)-- */ } } @@ -1396,7 +1382,6 @@ Perl_newHVhv(pTHX_ HV *ohv) } HvMAX(hv) = hv_max; - HvFILL(hv) = HvFILL(ohv); HvTOTALKEYS(hv) = HvTOTALKEYS(ohv); HvARRAY(hv) = ents; } /* not magical */ @@ -1639,8 +1624,6 @@ S_clear_placeholders(pTHX_ HV *hv, U32 items) while ((entry = *oentry)) { if (HeVAL(entry) == &PL_sv_placeholder) { *oentry = HeNEXT(entry); - if (first && !*oentry) - HvFILL(hv)--; /* This linked list is now empty. */ if (entry == HvEITER_get(hv)) HvLAZYDEL_on(hv); else @@ -1783,7 +1766,6 @@ S_hfreeentries(pTHX_ HV *hv) /* make everyone else think the array is empty, so that the destructors * called for freed entries can't recusively mess with us */ HvARRAY(hv) = NULL; - HvFILL(hv) = 0; ((XPVHV*) SvANY(hv))->xhv_keys = 0; @@ -1877,6 +1859,37 @@ Perl_hv_undef(pTHX_ HV *hv) mg_clear(MUTABLE_SV(hv)); } +/* +=for apidoc hv_fill + +Returns the number of hash buckets that happen to be in use. This function is +wrapped by the macro C. + +Previously this value was stored in the HV structure, rather than being +calculated on demand. + +=cut +*/ + +STRLEN +Perl_hv_fill(pTHX_ HV const *const hv) +{ + STRLEN count = 0; + HE **ents = HvARRAY(hv); + + PERL_ARGS_ASSERT_HV_FILL; + + if (ents) { + HE *const *const end = ents + HvMAX(hv); + + do { + if (*ents) + ++count; + } while (++ents <= end); + } + return count; +} + static struct xpvhv_aux* S_hv_auxinit(HV *hv) { struct xpvhv_aux *iter; @@ -2428,10 +2441,6 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash) if (entry) { if (--entry->he_valu.hent_refcount == 0) { *oentry = HeNEXT(entry); - if (!*first) { - /* There are now no entries in our slot. */ - xhv->xhv_fill--; /* HvFILL(hv)-- */ - } Safefree(entry); xhv->xhv_keys--; /* HvTOTALKEYS(hv)-- */ } @@ -2551,7 +2560,6 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags) xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */ if (!next) { /* initial entry? */ - xhv->xhv_fill++; /* HvFILL(hv)++ */ } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) { hsplit(PL_strtab); } @@ -2733,10 +2741,6 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain) /* Link it into the chain. */ HeNEXT(entry) = *oentry; - if (!HeNEXT(entry)) { - /* initial entry. */ - HvFILL(hv)++; - } *oentry = entry; HvTOTALKEYS(hv)++; diff --git a/hv.h b/hv.h index db79b46..0303b9f 100644 --- a/hv.h +++ b/hv.h @@ -235,7 +235,7 @@ C. # define Nullhv Null(HV*) #endif #define HvARRAY(hv) ((hv)->sv_u.svu_hash) -#define HvFILL(hv) ((XPVHV*) SvANY(hv))->xhv_fill +#define HvFILL(hv) Perl_hv_fill(aTHX_ (const HV *)(hv)) #define HvMAX(hv) ((XPVHV*) SvANY(hv))->xhv_max /* This quite intentionally does no flag checking first. That's your responsibility. */ diff --git a/perl.c b/perl.c index 7a87120..5b61f94 100644 --- a/perl.c +++ b/perl.c @@ -1115,7 +1115,6 @@ perl_destruct(pTHXx) Safefree(array); HvARRAY(PL_strtab) = 0; HvTOTALKEYS(PL_strtab) = 0; - HvFILL(PL_strtab) = 0; } SvREFCNT_dec(PL_strtab); diff --git a/proto.h b/proto.h index fef2fcb..8d4f283 100644 --- a/proto.h +++ b/proto.h @@ -1028,6 +1028,11 @@ PERL_CALLCONV void* Perl_hv_common_key_len(pTHX_ HV *hv, const char *key, I32 kl #define PERL_ARGS_ASSERT_HV_COMMON_KEY_LEN \ assert(key) +PERL_CALLCONV STRLEN Perl_hv_fill(pTHX_ HV const *const hv) + __attribute__nonnull__(pTHX_1); +#define PERL_ARGS_ASSERT_HV_FILL \ + assert(hv) + PERL_CALLCONV void Perl_hv_free_ent(pTHX_ HV *hv, HE *entryK) __attribute__nonnull__(pTHX_1); #define PERL_ARGS_ASSERT_HV_FREE_ENT \ diff --git a/sv.c b/sv.c index 6a0916f..eff293f 100644 --- a/sv.c +++ b/sv.c @@ -1339,13 +1339,6 @@ Perl_sv_upgrade(pTHX_ register SV *const sv, svtype new_type) HvSHAREKEYS_on(sv); /* key-sharing on by default */ #endif HvMAX(sv) = 7; /* (start with 8 buckets) */ - if (old_type_details->body_size) { - HvFILL(sv) = 0; - } else { - /* It will have been zeroed when the new body was allocated. - Lets not write to it, in case it confuses a write-back - cache. */ - } } /* SVt_NULL isn't the only thing upgraded to AV or HV.