Make HvFILL() count the allocated buckets, instead of reading a stored value.
Nicholas Clark [Sun, 24 Jan 2010 15:07:50 +0000 (15:07 +0000)]
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.

embed.fnc
global.sym
hv.c
hv.h
perl.c
proto.h
sv.c

index 00629fd..1f2ecb9 100644 (file)
--- 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
index 5cbfe3f..6cffeb8 100644 (file)
@@ -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 (file)
--- 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<HvFILL>.
+
+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 (file)
--- a/hv.h
+++ b/hv.h
@@ -235,7 +235,7 @@ C<SV*>.
 #  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 (file)
--- 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 (file)
--- 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 (file)
--- 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.