Nicholas Clark wrote:
On Tue, Mar 07, 2006 at 10:47:26PM +0000, Dave Mitchell wrote:
On Tue, Mar 07, 2006 at 11:45:01AM -0800, Nicholas Clark wrote:
+=head2 Allocate OPs from arenas
Erm, isn't the code already there, if PL_OP_SLAB_ALLOC is defined?
Er. I thought. I'd not looked at that.
And then, no, that's not what I was thinking about. It seems to allocate all
kinds of ops from a big slab of memory, and has fairly complex routines to
knock it down into chunks of the correct size. I was thinking of how the SV
body arena code works, which has 1 slap per type, and maintains a free list
for each type, which makes freeing and reallocation much simpler, and
allocation a bit simpler than the slab code.
Nicholas Clark
If I got you right, youre thinking of arena_roots for UNOPs, BINOPs,
PMOPs, COPs etc, from which OP-trees are built. This sounds bad from a
cache proximity standpoint, despite the relative simplicity vs the
OP_SLAB_ALLOC stuff.
Also, OPs dont get recycled as often as sv-bodies are (efficiency at
this is presumably important in why arenas are good)
Anyway, Ive worked a couple of patches towards improving arena flexibility:
1. split arena-table out of body-details (done in diff.arena-details-2)
Then body-details can be readonly, arena-details can be tweaked via
api. arena-details table should be alloc'd, and possibly shared
across threads with COW. Then a thread could a: set arenas for worker
threads, spawn all the worker threads, then change arenas for manager.
and it just seems cleaner.
2. (diff.arena-map-size-1)
#define MAP_SIZE(type) (type) /* identity map for now */
&PL_body_roots[MAP_SIZE(sv_type)] /* use map */
This can (but doesnt now) change the body-root used to supply bodies of
a given type.
3. notional 3rd patch.
the identity map is simplest, fastest. Not much waste in 1/2 used
arenas, since there arent that many sharable sizes.
16 svtypes, 5 OP-types
Building the svtype => reserved_slot_of(bodysize) map at runtime is
easy, except that this must be done at C begin-time, before any arenas
are needed. The map could be seeded, kinda like HASH_SEED.
a static map might be better (no runtime indirection) than one built at
runtime, but its tedious to do with macros (I dont see how).
a hybrid approach - with svtypes identity-mapped, using S_new_body,
new_body_inline, new_body_allocated, del_body, as is currently done ..
+ register_arena_user(size) could claim slots >= SVt_LAST (to some
undetermined max), allowing new*OP()s to allocate from arenas too (these
users get a set of macros similar to new_body_inline,
new_body_allocated, del_body).
This approach should make it reasonably easy to add OP-arenas,
then we can test the cache effect.
FWIW, the sharing of arenas for BINOPs, LISTOPs, LOGOPs should improve
the cache proximity, since sequences of those ops would be alloc'd out
of the same arena. (and presumably consecutive).
comments?
Binary files ar-0/ext/SDBM_File/sdbm/libsdbm.a and
ar-1/ext/SDBM_File/sdbm/libsdbm.a differ
Binary files ar-0/lib/auto/DynaLoader/DynaLoader.a and
ar-1/lib/auto/DynaLoader/DynaLoader.a differ
Binary files ar-0/libperl.a and ar-1/libperl.a differ
Binary files ar-0/miniperl and ar-1/miniperl differ
Binary files ar-0/perl and ar-1/perl differ
diff -ruNp -X exclude-diffs ar-0/sv.c ar-1/sv.c
--- ar-0/sv.c 2006-03-07 12:32:26.000000000 -0700
+++ ar-1/sv.c 2006-03-08 00:06:53.000000000 -0700
@@ -821,6 +821,9 @@ struct body_details {
unsigned int type : 4; /* We have space for a sanity check. */
unsigned int cant_upgrade : 1; /* Cannot upgrade this type */
unsigned int zero_nv : 1; /* zero the NV when upgrading from this */
+};
+
+struct arena_details {
unsigned int arena : 1; /* Allocated from an arena */
size_t arena_size; /* Size of arena to allocate */
};
@@ -885,85 +888,87 @@ struct xpv {
STRUCT_OFFSET(type, last_member) \
+ sizeof (((type*)SvANY((SV*)0))->last_member)
+/* Body-arena parameters, by sv_type. Sv-types w/o bodies dont need
+ arenas, so those slots are reused for other internal items: HE, PTR
+*/
+struct arena_details arenas_by_type[] = {
+
+ { NOARENA, FIT_ARENA(0, sizeof(HE)) },
+ /* IVS don't need an arena, so reuse for PTEs */
+ { NOARENA, FIT_ARENA(0, sizeof(struct ptr_tbl_ent)) },
+ { HASARENA, FIT_ARENA(0, sizeof(NV)) },
+ { NOARENA, 0 },
+ { HASARENA, FIT_ARENA(0, sizeof(xpv_allocated)) },
+ { HASARENA, FIT_ARENA(0, sizeof(xpviv_allocated)) },
+ { HASARENA, FIT_ARENA(0, sizeof(XPVNV)) },
+ { HASARENA, FIT_ARENA(0, sizeof(XPVMG)) },
+ { HASARENA, FIT_ARENA(0, sizeof(XPVBM)) },
+ { HASARENA, FIT_ARENA(0, sizeof(XPVGV)) },
+ { HASARENA, FIT_ARENA(0, sizeof(XPVLV)) },
+ { HASARENA, FIT_ARENA(0, sizeof(xpvav_allocated)) },
+ { HASARENA, FIT_ARENA(0, sizeof(xpvhv_allocated)) },
+ { HASARENA, FIT_ARENA(0, sizeof(xpvcv_allocated)) },
+ { NOARENA, FIT_ARENA(20, sizeof(xpvfm_allocated)) },
+ { HASARENA, FIT_ARENA(24, sizeof(XPVIO)) },
+};
+
static const struct body_details bodies_by_type[] = {
- { sizeof(HE), 0, 0, SVt_NULL,
- FALSE, NONV, NOARENA, FIT_ARENA(0, sizeof(HE)) },
+ { sizeof(HE), 0, 0, SVt_NULL, FALSE, NONV },
- /* IVs are in the head, so the allocation size is 0.
- However, the slot is overloaded for PTEs. */
{ sizeof(struct ptr_tbl_ent), /* This is used for PTEs. */
sizeof(IV), /* This is used to copy out the IV body. */
STRUCT_OFFSET(XPVIV, xiv_iv), SVt_IV, FALSE, NONV,
- NOARENA /* IVS don't need an arena */,
- /* But PTEs need to know the size of their arena */
- FIT_ARENA(0, sizeof(struct ptr_tbl_ent))
},
/* 8 bytes on most ILP32 with IEEE doubles */
- { sizeof(NV), sizeof(NV), 0, SVt_NV, FALSE, HADNV, HASARENA,
- FIT_ARENA(0, sizeof(NV)) },
+ { sizeof(NV), sizeof(NV), 0, SVt_NV, FALSE, HADNV },
- /* RVs are in the head now. */
- { 0, 0, 0, SVt_RV, FALSE, NONV, NOARENA, 0 },
+ /* RVs are in the head, so theres no body to upgrade */
+ { 0, 0, 0, SVt_RV, FALSE, NONV },
/* 8 bytes on most ILP32 with IEEE doubles */
{ sizeof(xpv_allocated),
copy_length(XPV, xpv_len)
- relative_STRUCT_OFFSET(xpv_allocated, XPV, xpv_cur),
+ relative_STRUCT_OFFSET(xpv_allocated, XPV, xpv_cur),
- SVt_PV, FALSE, NONV, HASARENA, FIT_ARENA(0, sizeof(xpv_allocated)) },
+ SVt_PV, FALSE, NONV },
/* 12 */
{ sizeof(xpviv_allocated),
copy_length(XPVIV, xiv_u)
- relative_STRUCT_OFFSET(xpviv_allocated, XPVIV, xpv_cur),
+ relative_STRUCT_OFFSET(xpviv_allocated, XPVIV, xpv_cur),
- SVt_PVIV, FALSE, NONV, HASARENA, FIT_ARENA(0, sizeof(xpviv_allocated)) },
+ SVt_PVIV, FALSE, NONV },
- /* 20 */
- { sizeof(XPVNV), copy_length(XPVNV, xiv_u), 0, SVt_PVNV, FALSE, HADNV,
- HASARENA, FIT_ARENA(0, sizeof(XPVNV)) },
-
- /* 28 */
- { sizeof(XPVMG), copy_length(XPVMG, xmg_stash), 0, SVt_PVMG, FALSE, HADNV,
- HASARENA, FIT_ARENA(0, sizeof(XPVMG)) },
-
- /* 36 */
- { sizeof(XPVBM), sizeof(XPVBM), 0, SVt_PVBM, TRUE, HADNV,
- HASARENA, FIT_ARENA(0, sizeof(XPVBM)) },
-
- /* 48 */
- { sizeof(XPVGV), sizeof(XPVGV), 0, SVt_PVGV, TRUE, HADNV,
- HASARENA, FIT_ARENA(0, sizeof(XPVGV)) },
-
- /* 64 */
- { sizeof(XPVLV), sizeof(XPVLV), 0, SVt_PVLV, TRUE, HADNV,
- HASARENA, FIT_ARENA(0, sizeof(XPVLV)) },
+ /* 20 */ /* 28 */
+ { sizeof(XPVNV), copy_length(XPVNV, xiv_u), 0, SVt_PVNV, FALSE, HADNV },
+ { sizeof(XPVMG), copy_length(XPVMG, xmg_stash), 0, SVt_PVMG, FALSE, HADNV},
+ /* 36 */ /* 48 */ /* 64 */
+ { sizeof(XPVBM), sizeof(XPVBM), 0, SVt_PVBM, TRUE, HADNV },
+ { sizeof(XPVGV), sizeof(XPVGV), 0, SVt_PVGV, TRUE, HADNV },
+ { sizeof(XPVLV), sizeof(XPVLV), 0, SVt_PVLV, TRUE, HADNV },
{ sizeof(xpvav_allocated),
copy_length(XPVAV, xmg_stash)
- relative_STRUCT_OFFSET(xpvav_allocated, XPVAV, xav_fill),
+ relative_STRUCT_OFFSET(xpvav_allocated, XPVAV, xav_fill),
- SVt_PVAV, TRUE, HADNV, HASARENA, FIT_ARENA(0, sizeof(xpvav_allocated)) },
+ SVt_PVAV, TRUE, HADNV },
{ sizeof(xpvhv_allocated),
copy_length(XPVHV, xmg_stash)
- relative_STRUCT_OFFSET(xpvhv_allocated, XPVHV, xhv_fill),
+ relative_STRUCT_OFFSET(xpvhv_allocated, XPVHV, xhv_fill),
- SVt_PVHV, TRUE, HADNV, HASARENA, FIT_ARENA(0, sizeof(xpvhv_allocated)) },
+ SVt_PVHV, TRUE, HADNV },
- /* 56 */
{ sizeof(xpvcv_allocated), sizeof(xpvcv_allocated),
+ relative_STRUCT_OFFSET(xpvcv_allocated, XPVCV, xpv_cur),
- SVt_PVCV, TRUE, NONV, HASARENA, FIT_ARENA(0, sizeof(xpvcv_allocated)) },
+ SVt_PVCV, TRUE, NONV },
{ sizeof(xpvfm_allocated), sizeof(xpvfm_allocated),
+ relative_STRUCT_OFFSET(xpvfm_allocated, XPVFM, xpv_cur),
- SVt_PVFM, TRUE, NONV, NOARENA, FIT_ARENA(20, sizeof(xpvfm_allocated)) },
+ SVt_PVFM, TRUE, NONV },
- /* XPVIO is 84 bytes, fits 48x */
- { sizeof(XPVIO), sizeof(XPVIO), 0, SVt_PVIO, TRUE, HADNV,
- HASARENA, FIT_ARENA(24, sizeof(XPVIO)) },
+ { sizeof(XPVIO), sizeof(XPVIO), 0, SVt_PVIO, TRUE, HADNV },
};
#define new_body_type(sv_type) \
@@ -1044,11 +1049,12 @@ S_more_bodies (pTHX_ svtype sv_type)
dVAR;
void ** const root = &PL_body_roots[sv_type];
const struct body_details * const bdp = &bodies_by_type[sv_type];
+ const struct arena_details * const adp = &arenas_by_type[sv_type];
const size_t body_size = bdp->body_size;
char *start;
const char *end;
- assert(bdp->arena_size);
+ assert(adp->arena_size);
#ifdef DEBUGGING
if (!done_sanity_check) {
@@ -1061,15 +1067,15 @@ S_more_bodies (pTHX_ svtype sv_type)
}
#endif
- start = (char*) Perl_get_arena(aTHX_ bdp->arena_size);
+ start = (char*) Perl_get_arena(aTHX_ adp->arena_size);
- end = start + bdp->arena_size - body_size;
+ end = start + adp->arena_size - body_size;
/* computed count doesnt reflect the 1st slot reservation */
DEBUG_m(PerlIO_printf(Perl_debug_log,
"arena %p end %p arena-size %d type %d size %d ct
%d\n",
- start, end, bdp->arena_size, sv_type, body_size,
- bdp->arena_size / body_size));
+ start, end, adp->arena_size, sv_type, body_size,
+ adp->arena_size / body_size));
*root = (void *)start;
@@ -1250,8 +1256,8 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U
assert(new_type_details->body_size);
#ifndef PURIFY
- assert(new_type_details->arena);
- assert(new_type_details->arena_size);
+ assert(arenas_by_type[new_type].arena);
+ assert(arenas_by_type[new_type].arena_size);
/* This points to the start of the allocated area. */
new_body_inline(new_body, new_type);
Zero(new_body, new_type_details->body_size, char);
@@ -1305,7 +1311,7 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U
assert(new_type_details->body_size);
/* We always allocated the full length item with PURIFY. To do this
we fake things so that arena is false for all 16 types.. */
- if(new_type_details->arena) {
+ if(arenas_by_type[new_type].arena) {
/* This points to the start of the allocated area. */
new_body_inline(new_body, new_type);
Zero(new_body, new_type_details->body_size, char);
@@ -1353,7 +1359,7 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U
(unsigned long)new_type);
}
- if (old_type_details->arena) {
+ if (arenas_by_type[old_type].arena) {
/* If there was an old body, then we need to free it.
Note that there is an assumption that all bodies of types that
can be upgraded came from arenas. Only the more complex non-
@@ -5134,7 +5140,7 @@ Perl_sv_clear(pTHX_ register SV *sv)
SvFLAGS(sv) &= SVf_BREAK;
SvFLAGS(sv) |= SVTYPEMASK;
- if (sv_type_details->arena) {
+ if (arenas_by_type[type].arena) {
del_body(((char *)SvANY(sv) + sv_type_details->offset),
&PL_body_roots[type]);
}
@@ -9743,7 +9749,7 @@ Perl_sv_dup(pTHX_ const SV *sstr, CLONE_
case SVt_PVIV:
case SVt_PV:
assert(sv_type_details->body_size);
- if (sv_type_details->arena) {
+ if (arenas_by_type[sv_type].arena) {
new_body_inline(new_body, sv_type);
new_body
= (void*)((char*)new_body - sv_type_details->offset);
Binary files ar-0/x2p/a2p and ar-1/x2p/a2p differ
Binary files ar-1/ext/SDBM_File/sdbm/libsdbm.a and
ar-2/ext/SDBM_File/sdbm/libsdbm.a differ
Binary files ar-1/lib/auto/DynaLoader/DynaLoader.a and
ar-2/lib/auto/DynaLoader/DynaLoader.a differ
Binary files ar-1/libperl.a and ar-2/libperl.a differ
Binary files ar-1/miniperl and ar-2/miniperl differ
Binary files ar-1/perl and ar-2/perl differ
diff -ruNp -X exclude-diffs ar-1/sv.c ar-2/sv.c
--- ar-1/sv.c 2006-03-08 00:47:03.000000000 -0700
+++ ar-2/sv.c 2006-03-08 02:07:04.000000000 -0700
@@ -630,6 +630,7 @@ Perl_sv_free_arenas(pTHX)
}
PL_body_arenas = 0;
+ /* limit is dynamic if mapped down by size */
for (i=0; i<PERL_ARENA_ROOTS_SIZE; i++)
PL_body_roots[i] = 0;
@@ -912,6 +913,8 @@ struct arena_details arenas_by_type[] =
{ HASARENA, FIT_ARENA(24, sizeof(XPVIO)) },
};
+#define map_size(a) a /* identity map, for starts */
+
static const struct body_details bodies_by_type[] = {
{ sizeof(HE), 0, 0, SVt_NULL, FALSE, NONV },
@@ -971,11 +974,14 @@ static const struct body_details bodies_
{ sizeof(XPVIO), sizeof(XPVIO), 0, SVt_PVIO, TRUE, HADNV },
};
+/* map svtype => sizeof(body) */
+#define MAP_SIZE(svtype) (svtype) /* identity map, for starts */
+
#define new_body_type(sv_type) \
(void *)((char *)S_new_body(aTHX_ sv_type))
#define del_body_type(p, sv_type) \
- del_body(p, &PL_body_roots[sv_type])
+ del_body(p, &PL_body_roots[MAP_SIZE(sv_type)])
#define new_body_allocated(sv_type) \
@@ -983,7 +989,8 @@ static const struct body_details bodies_
- bodies_by_type[sv_type].offset)
#define del_body_allocated(p, sv_type) \
- del_body(p + bodies_by_type[sv_type].offset, &PL_body_roots[sv_type])
+ del_body(p + bodies_by_type[sv_type].offset, \
+ &PL_body_roots[MAP_SIZE(sv_type)])
#define my_safemalloc(s) (void*)safemalloc(s)
@@ -1047,7 +1054,7 @@ STATIC void *
S_more_bodies (pTHX_ svtype sv_type)
{
dVAR;
- void ** const root = &PL_body_roots[sv_type];
+ void ** const root = &PL_body_roots[MAP_SIZE(sv_type)];
const struct body_details * const bdp = &bodies_by_type[sv_type];
const struct arena_details * const adp = &arenas_by_type[sv_type];
const size_t body_size = bdp->body_size;
@@ -1095,7 +1102,7 @@ S_more_bodies (pTHX_ svtype sv_type)
*/
#define new_body_inline(xpv, sv_type) \
STMT_START { \
- void ** const r3wt = &PL_body_roots[sv_type]; \
+ void ** const r3wt = &PL_body_roots[MAP_SIZE(sv_type)]; \
LOCK_SV_MUTEX; \
xpv = *((void **)(r3wt)) \
? *((void **)(r3wt)) : S_more_bodies(aTHX_ sv_type); \
@@ -1368,7 +1375,7 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U
my_safefree(old_body);
#else
del_body((void*)((char*)old_body + old_type_details->offset),
- &PL_body_roots[old_type]);
+ &PL_body_roots[MAP_SIZE(old_type)]);
#endif
}
}
@@ -5142,7 +5149,7 @@ Perl_sv_clear(pTHX_ register SV *sv)
if (arenas_by_type[type].arena) {
del_body(((char *)SvANY(sv) + sv_type_details->offset),
- &PL_body_roots[type]);
+ &PL_body_roots[MAP_SIZE(type)]);
}
else if (sv_type_details->body_size) {
my_safefree(SvANY(sv));