new_body_type doesn't need to subtract the offset, that's what
[p5sagit/p5-mst-13.2.git] / sv.c
diff --git a/sv.c b/sv.c
index 0fc488d..36cea18 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -556,6 +556,52 @@ Perl_sv_clean_all(pTHX)
     return cleaned;
 }
 
+/*
+  ARENASETS: a meta-arena implementation which separates arena-info
+  into struct arena_set, which contains an array of struct
+  arena_descs, each holding info for a single arena.  By separating
+  the meta-info from the arena, we recover the 1st slot, formerly
+  borrowed for list management.  The arena_set is about the size of an
+  arena, avoiding the needless malloc overhead of a naive linked-list
+
+  The cost is 1 arena-set malloc per ~320 arena-mallocs, + the unused
+  memory in the last arena-set (1/2 on average).  In trade, we get
+  back the 1st slot in each arena (ie 1.7% of a CV-arena, less for
+  others)
+
+  union arena is declared with a fixed size, but is intended to vary
+  by type, allowing their use for big, rare body-types where theres
+  currently too much wastage (unused arena slots)
+*/
+#define ARENASETS 1
+
+struct arena_desc {
+    char       *arena;         /* the raw storage, allocated aligned */
+    size_t      size;          /* its size ~4k typ */
+    int         unit_type;     /* useful for arena audits */
+    /* info for sv-heads (eventually)
+       int count, flags;
+    */
+};
+
+struct arena_set;
+
+/* Get the maximum number of elements in set[] such that struct arena_set
+   will fit within PERL_ARENA_SIZE, which is probabably just under 4K, and
+   therefore likely to be 1 aligned memory page.  */
+
+#define ARENAS_PER_SET  ((PERL_ARENA_SIZE - sizeof(struct arena_set*) \
+                         - 2 * sizeof(int)) / sizeof (struct arena_desc))
+
+struct arena_set {
+    struct arena_set* next;
+    int   set_size;            /* ie ARENAS_PER_SET */
+    int   curr;                        /* index of next available arena-desc */
+    struct arena_desc set[ARENAS_PER_SET];
+};
+
+#if !ARENASETS
+
 static void 
 S_free_arena(pTHX_ void **root) {
     while (root) {
@@ -564,7 +610,8 @@ S_free_arena(pTHX_ void **root) {
        root = next;
     }
 }
-    
+#endif
+
 /*
 =for apidoc sv_free_arenas
 
@@ -593,7 +640,23 @@ Perl_sv_free_arenas(pTHX)
            Safefree(sva);
     }
 
+#if ARENASETS
+    {
+       struct arena_set *next, *aroot = (struct arena_set*) PL_body_arenas;
+       
+       for (; aroot; aroot = next) {
+           int max = aroot->curr;
+           for (i=0; i<max; i++) {
+               assert(aroot->set[i].arena);
+               Safefree(aroot->set[i].arena);
+           }
+           next = aroot->next;
+           Safefree(aroot);
+       }
+    }
+#else
     S_free_arena(aTHX_ (void**) PL_body_arenas);
+#endif
 
     for (i=0; i<SVt_LAST; i++)
        PL_body_roots[i] = 0;
@@ -640,6 +703,61 @@ Perl_sv_free_arenas(pTHX)
   contexts below (line ~10k)
 */
 
+/* get_arena(size): when ARENASETS is enabled, this creates
+   custom-sized arenas, otherwize it uses PERL_ARENA_SIZE, as
+   previously done.
+   TBD: export properly for hv.c: S_more_he().
+*/
+void*
+Perl_get_arena(pTHX_ int arena_size)
+{
+#if !ARENASETS
+    union arena* arp;
+
+    /* allocate and attach arena */
+    Newx(arp, PERL_ARENA_SIZE, char);
+    arp->next = PL_body_arenas;
+    PL_body_arenas = arp;
+    return arp;
+
+#else
+    struct arena_desc* adesc;
+    struct arena_set *newroot, *aroot = (struct arena_set*) PL_body_arenas;
+    int curr;
+
+    if (!arena_size)
+       arena_size = PERL_ARENA_SIZE;
+
+    /* may need new arena-set to hold new arena */
+    if (!aroot || aroot->curr >= aroot->set_size) {
+       Newxz(newroot, 1, struct arena_set);
+       newroot->set_size = ARENAS_PER_SET;
+       newroot->next = aroot;
+       aroot = newroot;
+       DEBUG_m(PerlIO_printf(Perl_debug_log, "new arenaset %p\n", aroot));
+    }
+
+    /* ok, now have arena-set with at least 1 empty/available arena-desc */
+    curr = aroot->curr++;
+    adesc = &aroot->set[curr];
+    assert(!adesc->arena);
+    
+    /* old fixed-size way
+       Newxz(adesc->arena, 1, union arena);
+       adesc->size = sizeof(union arena);
+    */
+    /* new buggy way    */
+    Newxz(adesc->arena, arena_size, char);
+    adesc->size = arena_size;
+
+    /* adesc->count = sizeof(struct arena)/size; */
+    
+    DEBUG_m(PerlIO_printf(Perl_debug_log, "arena %d added: %p\n", curr, aroot));
+
+    return adesc->arena;
+#endif
+}
+
 STATIC void *
 S_more_bodies (pTHX_ size_t size, svtype sv_type)
 {
@@ -649,16 +767,15 @@ S_more_bodies (pTHX_ size_t size, svtype sv_type)
     const char *end;
     const size_t count = PERL_ARENA_SIZE / size;
 
-    Newx(start, count*size, char);
-    *((void **) start) = PL_body_arenas;
-    PL_body_arenas = (void *)start;
+    start = (char*) Perl_get_arena(aTHX_ PERL_ARENA_SIZE); /* get a raw arena */
 
     end = start + (count-1) * size;
 
+#if !ARENASETS
     /* The initial slot is used to link the arenas together, so it isn't to be
        linked into the list of ready-to-use bodies.  */
-
     start += size;
+#endif
 
     *root = (void *)start;
 
@@ -853,8 +970,7 @@ static const struct body_details bodies_by_type[] = {
 };
 
 #define new_body_type(sv_type)                 \
-    (void *)((char *)S_new_body(aTHX_ bodies_by_type[sv_type].size, sv_type)\
-            - bodies_by_type[sv_type].offset)
+    (void *)((char *)S_new_body(aTHX_ bodies_by_type[sv_type].size, sv_type))
 
 #define del_body_type(p, sv_type)      \
     del_body(p, &PL_body_roots[sv_type])
@@ -1037,9 +1153,10 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
     SvFLAGS(sv) &= ~SVTYPEMASK;
     SvFLAGS(sv) |= new_type;
 
+    /* This can't happen, as SVt_NULL is <= all values of new_type, so one of
+       the return statements above will have triggered.  */
+    assert (new_type != SVt_NULL);
     switch (new_type) {
-    case SVt_NULL:
-       Perl_croak(aTHX_ "Can't upgrade to undef");
     case SVt_IV:
        assert(old_type == SVt_NULL);
        SvANY(sv) = (XPVIV*)((char*)&(sv->sv_u.svu_iv) - STRUCT_OFFSET(XPVIV, xiv_iv));
@@ -1056,21 +1173,27 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
        SvRV_set(sv, 0);
        return;
     case SVt_PVHV:
-       SvANY(sv) = new_XPVHV();
-       HvFILL(sv)      = 0;
-       HvMAX(sv)       = 0;
-       HvTOTALKEYS(sv) = 0;
-
-       goto hv_av_common;
-
     case SVt_PVAV:
-       SvANY(sv) = new_XPVAV();
-       AvMAX(sv)       = -1;
-       AvFILLp(sv)     = -1;
-       AvALLOC(sv)     = 0;
-       AvREAL_only(sv);
+       assert(new_type_details->size);
+
+#ifndef PURIFY 
+       assert(new_type_details->arena);
+       /* This points to the start of the allocated area.  */
+       new_body_inline(new_body, new_type_details->size, new_type);
+       Zero(new_body, new_type_details->size, char);
+       new_body = ((char *)new_body) - new_type_details->offset;
+#else
+       /* We always allocated the full length item with PURIFY. To do this
+          we fake things so that arena is false for all 16 types..  */
+       new_body = new_NOARENAZ(new_type_details);
+#endif
+       SvANY(sv) = new_body;
+       if (new_type == SVt_PVAV) {
+           AvMAX(sv)   = -1;
+           AvFILLp(sv) = -1;
+           AvREAL_only(sv);
+       }
 
-    hv_av_common:
        /* SVt_NULL isn't the only thing upgraded to AV or HV.
           The target created by newSVrv also is, and it can have magic.
           However, it never has SvPVX set.
@@ -1086,9 +1209,6 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
        if (old_type >= SVt_PVMG) {
            SvMAGIC_set(sv, ((XPVMG*)old_body)->xmg_magic);
            SvSTASH_set(sv, ((XPVMG*)old_body)->xmg_stash);
-       } else {
-           SvMAGIC_set(sv, NULL);
-           SvSTASH_set(sv, NULL);
        }
        break;
 
@@ -6875,8 +6995,11 @@ Perl_sv_2cv(pTHX_ SV *sv, HV **st, GV **gvp, I32 lref)
     GV *gv = NULL;
     CV *cv = NULL;
 
-    if (!sv)
-       return *st = NULL, *gvp = NULL, NULL;
+    if (!sv) {
+       *st = NULL;
+       *gvp = NULL;
+       return NULL;
+    }
     switch (SvTYPE(sv)) {
     case SVt_PVCV:
        *st = CvSTASH(sv);
@@ -10278,6 +10401,8 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     param->flags = flags;
     param->proto_perl = proto_perl;
 
+    INIT_TRACK_MEMPOOL(my_perl->Imemory_debug_header, my_perl);
+
     PL_body_arenas = NULL;
     Zero(&PL_body_roots, 1, PL_body_roots);