Key features are:
1) DOD flags (live, on_free_list, ...) are kept in the arenas, 1 nibble per object
2) arena memory is aquired per memalign() to be able to calculate arena from object address
3) free_list is per arena now
4) PMC size is 20 byte (i386, GC_DEBUG turned off) for ultimate speed
1) bis 3) can be toggled in include/parrot/pobj.h with #define ARENA_DOD_FLAGS 1
Turning this define on has currently rather low impact on performance on my system, only stress3.pasm seems to indicate, that it is the Right Thing (IMHO).
With ARENA_DOD_FLAGS turned on, objects are not touched in the fast paths of DOD. Only objects that get destroyed are pulled into the cache - but our free_list handling does this anyway. This reduces cache misses by a fair amount.
Keeping the free_list per arena should give better cache coherency as add_free_object/get_free_object deal with objects of one arena only and not with all objects in the whole pool. This effect should show up in long running programs, when the free_list(s) gets more and more scattered other arena/pool memory.
In dod.c there is also code to free unused arenas (when a program first allocates a lot of objects and then has a low usage count) But this doesn't improve performance - though it should IMHO ;-)
Calculating the location of the DOD flags is of course rather costy compared to just touching the objects itself. But as processors get faster, avoiding memory accesses (and cache misses) becomes more important, so I think, this is the way to go.
Test results especially on fast machines welcome. Comments welcome.
TIA & have fun, leo
--- parrot/classes/default.pmc Mon May 19 15:07:49 2003 +++ parrot-leo/classes/default.pmc Sun May 25 14:12:25 2003 @@ -48,7 +48,7 @@ PMC* getprop(STRING* key) { PMC* p_key = key_new_string(interpreter, key); - if (SELF->metadata) { + if (SELF->pmc_ext && SELF->metadata) { return (VTABLE_get_pmc_keyed(interpreter, SELF->metadata, p_key)); } else { PMC* undef = pmc_new(INTERP, enum_class_PerlUndef); @@ -59,11 +59,13 @@ void setprop(STRING* key, PMC* value) { PMC* p_key; - if (SELF->metadata) { + if (SELF->pmc_ext && SELF->metadata) { p_key = key_new_string(interpreter, key); VTABLE_set_pmc_keyed(interpreter, SELF->metadata, p_key, value, NULL); } else { + if (!SELF->pmc_ext) + add_pmc_ext(INTERP, SELF); /* first make new hash */ SELF->metadata = pmc_new_noinit(interpreter, enum_class_PerlHash); VTABLE_init(interpreter, SELF->metadata); @@ -76,7 +78,7 @@ } void delprop(STRING* key) { - if (SELF->metadata) { + if (SELF->pmc_ext && SELF->metadata) { PMC* p_key = key_new_string(interpreter, key); VTABLE_delete_keyed(interpreter, SELF->metadata, p_key); } @@ -84,6 +86,8 @@ } PMC* getprops() { + if (!SELF->pmc_ext) + add_pmc_ext(INTERP, SELF); if (!SELF->metadata) { SELF->metadata = pmc_new_noinit(interpreter, enum_class_PerlHash); VTABLE_init(interpreter, SELF->metadata); --- parrot/dod.c Wed May 21 16:59:10 2003 +++ parrot-leo/dod.c Thu May 29 13:16:37 2003 @@ -31,6 +31,37 @@ static size_t find_common_mask(size_t val1, size_t val2); +#if ARENA_DOD_FLAGS + +void pobject_lives(struct Parrot_Interp *interpreter, PObj *obj) +{ + + struct Small_Object_Arena *arena = GET_ARENA(obj); + size_t n = GET_OBJ_N(arena, obj); + size_t ns = n >> ARENA_FLAG_SHIFT; + UINTVAL nm = (n & ARENA_FLAG_MASK) << 2; + UINTVAL *dod_flags = arena->dod_flags + ns; + if (*dod_flags & ((PObj_on_free_list_FLAG | PObj_live_FLAG) << nm)) + return; + ++arena->live_objects; + *dod_flags |= PObj_live_FLAG << nm; + + if (*dod_flags & (PObj_is_special_PMC_FLAG << nm)) { + if (((PMC*)obj)->pmc_ext) { + /* put it on the end of the list */ + interpreter->mark_ptr->next_for_GC = (PMC *)obj; + /* Explicitly make the tail of the linked list be + * self-referential */ + interpreter->mark_ptr = ((PMC*)obj)->next_for_GC = (PMC *)obj; + } + else if (PObj_custom_mark_TEST(obj)) + VTABLE_mark(interpreter, (PMC *) obj); + return; + } +} + +#else + /* Tag a buffer header as alive. Used by the GC system when tracing * the root set, and used by the PMC GC handling routines to tag their * individual pieces if they have private ones */ @@ -55,16 +86,16 @@ /* if object is a PMC and contains buffers or PMCs, then attach * the PMC to the chained mark list */ - if (PObj_is_PMC_TEST(obj)) { - UINTVAL mask = PObj_is_PMC_ptr_FLAG | PObj_is_buffer_ptr_FLAG - | PObj_custom_mark_FLAG; - if ( (PObj_get_FLAGS(obj) & mask) || ((PMC*)obj)->metadata) { + if (PObj_is_special_PMC_TEST(obj)) { + if (((PMC*)obj)->pmc_ext) { /* put it on the end of the list */ interpreter->mark_ptr->next_for_GC = (PMC *)obj; /* Explicitly make the tail of the linked list be * self-referential */ interpreter->mark_ptr = ((PMC*)obj)->next_for_GC = (PMC *)obj; } + else if (PObj_custom_mark_TEST(obj)) + VTABLE_mark(interpreter, (PMC *) obj); return; } #if GC_VERBOSE @@ -79,6 +110,8 @@ #endif } +#endif + /* Do a full trace run and mark all the PMCs as active if they are */ static void @@ -317,6 +350,75 @@ } #endif /* GC_IS_MALLOC */ +#if ARENA_DOD_FLAGS +static void +clear_live_counter(struct Parrot_Interp *interpreter, + struct Small_Object_Pool *pool) +{ + struct Small_Object_Arena *arena; + for (arena = pool->last_Arena; arena; arena = arena->prev) + arena->live_objects = 0; +} + +# define REDUCE_ARENAS 0 + +# if REDUCE_ARENAS +/* -lt: + * count free objects per arena + * - if we find more then one totally unused arena + * free all but one arena - this is the only possibility to + * reduce the amount of free objects + * + * doesn't really work or speed things up - disabled + */ +# define REDUCE_ARENA_DEBUG 0 + +#if REDUCE_ARENA_DEBUG +#define debug(x) printf x +#else +#define debug(x) +#endif + +static void +reduce_arenas(struct Parrot_Interp *interpreter, + struct Small_Object_Pool *pool, UINTVAL free_arenas) +{ + struct Small_Object_Arena *arena, *next, *prev; + int i; + PObj * o; + + /* debugging stuff */ + for (i = 0, arena = pool->last_Arena; arena; arena = arena->prev) + i++; + debug(("\ttotal %d arenas - ", i)); + + for (next = arena = pool->last_Arena; arena; ) { + prev = arena->prev; + if (arena->live_objects == 0) { + if (--free_arenas <= 0) + break; + next->prev = prev; + pool->total_objects -= arena->used; +#if ! ARENA_DOD_FLAGS + mem_sys_free(arena->start_objects); +#endif + mem_sys_free(arena); + + } + else { + next = arena; + } + arena = prev; + } + for (i = 0, arena = pool->last_Arena; arena; arena = arena->prev) + i++; + debug(("now %d arenas\n", i)); + +} + +# endif +#endif + /* Put any buffers/PMCs that are now unused, on to the pools free list. * If GC_IS_MALLOC bufstart gets freed too if possible. * Avoid buffers that are immune from collection (ie, constant) */ @@ -328,6 +430,10 @@ UINTVAL i, total_used = 0; UINTVAL object_size = pool->object_size; UINTVAL wash_size = object_size - sizeof(Buffer); + size_t nm; +#if REDUCE_ARENAS + UINTVAL free_arenas = 0, old_total_used = 0; +#endif /* We have no impatient things. Yet. */ interpreter->impatient_things = 0; @@ -337,12 +443,35 @@ NULL != cur_arena; cur_arena = cur_arena->prev) { Buffer *b = cur_arena->start_objects; - for (i = 0; i < cur_arena->used; i++) { +#if ARENA_DOD_FLAGS + UINTVAL * dod_flags = cur_arena->dod_flags - 1; +#endif + for (i = nm = 0; i < cur_arena->used; i++) { /* If it's not live or on the free list, put it on the free list. * Note that it is technically possible to have a Buffer be both * on_free_list and live, because of our conservative stack-walk * collection. We must be wary of this case. */ - if (!PObj_is_live_or_free_TESTALL(b)) { +#if ARENA_DOD_FLAGS + if (! (i & ARENA_FLAG_MASK)) { + if (i) + *dod_flags &= ~ALL_LIVE_MASK; + ++dod_flags; + if (*dod_flags == ALL_FREE_MASK) { /* all on free list */ + i += ARENA_FLAG_MASK; + b = (Buffer *)((char *)b + object_size*(ARENA_FLAG_MASK+1)); + continue; + } + nm = 0; + } + else + nm += 4; + + if (! (*dod_flags & + ( (PObj_live_FLAG|PObj_on_free_list_FLAG) << nm)) ) +#else + if (!PObj_is_live_or_free_TESTALL(b)) +#endif + { #if GC_VERBOSE if (GC_DEBUG(interpreter) && PObj_report_TEST(b)) fprintf(stderr, "Freeing pobject %p -> %p\n", @@ -355,12 +484,28 @@ */ if (PObj_active_destroy_TEST(b)) VTABLE_destroy(interpreter, (PMC *)b); + + if (PObj_is_PMC_EXT_TEST(b)) { +#if ARENA_DOD_FLAGS + struct Small_Object_Arena *ext_arena = + GET_ARENA(((PMC *)b)->pmc_ext); + ext_arena->pool-> + add_free_object(interpreter, ext_arena, + ((PMC *)b)->pmc_ext); +#else + struct Small_Object_Pool *ext_pool = + interpreter->arena_base->pmc_ext_pool; + ext_pool->add_free_object(interpreter, ext_pool, + ((PMC *)b)->pmc_ext); +#endif + } } /* else object is a buffer(like) */ else if (PObj_sysmem_TEST(b) && b->bufstart) { /* has sysmem allocated, e.g. string_pin */ mem_sys_free(b->bufstart); memset(b + 1, 0, wash_size); + b->buflen = 0; } else { #ifdef GC_IS_MALLOC @@ -392,21 +537,69 @@ * PMC specific data members */ memset(b + 1, 0, wash_size); + b->buflen = 0; } +#if ARENA_DOD_FLAGS + *dod_flags |= PObj_on_free_list_FLAG << nm; + pool->add_free_object(interpreter, cur_arena, b); +#else pool->add_free_object(interpreter, pool, b); +#endif } - else if (!PObj_on_free_list_TEST(b)) { +#if ARENA_DOD_FLAGS + else if (! (*dod_flags & (PObj_on_free_list_FLAG << nm))) +#else + else if (!PObj_on_free_list_TEST(b)) +#endif + { /* should be live then */ total_used++; +#if ARENA_DOD_FLAGS + if ((*dod_flags & (PObj_is_impatient_FLAG << nm))) + interpreter->impatient_things = 1; +#else PObj_live_CLEAR(b); - if (PObj_is_impatient_TEST(b)) { - interpreter->impatient_things++; - } + if (PObj_is_impatient_TEST(b)) + interpreter->impatient_things = 1; +#endif } b = (Buffer *)((char *)b + object_size); } +#if ARENA_DOD_FLAGS + *dod_flags &= ~ALL_LIVE_MASK; +# if REDUCE_ARENAS + /* not strictly only for ARENA_DOD_FLAGS, but + * live_objects is only defined there + */ + if ( (cur_arena->live_objects = total_used - old_total_used) == 0) + ++free_arenas; + old_total_used = total_used; +# endif +#endif } pool->num_free_objects = pool->total_objects - total_used; +#if ARENA_DOD_FLAGS +# if REDUCE_ARENAS +#define REPLENISH_LEVEL_FACTOR 0.3 + if (free_arenas > 1) { + debug(("pool %s: %d free_arenas\n", pool->name, (int)free_arenas)); + pool->replenish_level = + (size_t)(pool->total_objects * REPLENISH_LEVEL_FACTOR); + i = 0; + while (pool->num_free_objects - i * pool->last_Arena->used * + REPLENISH_LEVEL_FACTOR > pool->replenish_level && + i < free_arenas) + ++i; + debug(("\t may free %d\n", (int)i)); + reduce_arenas(interpreter, pool, i-1); + pool->replenish_level = + (size_t)(pool->total_objects * REPLENISH_LEVEL_FACTOR); + pool->num_free_objects = pool->total_objects - total_used; + pool->skip = 0; + } +# undef debug +# endif +#endif } #ifndef PLATFORM_STACK_WALK @@ -503,6 +696,14 @@ } Parrot_block_DOD(interpreter); +#if ARENA_DOD_FLAGS + clear_live_counter(interpreter, interpreter->arena_base->pmc_pool); + for (j = 0; j < (INTVAL)interpreter->arena_base->num_sized; j++) { + header_pool = interpreter->arena_base->sized_header_pools[j]; + if (header_pool) + clear_live_counter(interpreter, header_pool); + } +#endif /* Now go trace the PMCs */ trace_active_PMCs(interpreter); --- parrot/examples/benchmarks/stress3.pasm Wed May 28 16:56:33 2003 +++ parrot-leo/examples/benchmarks/stress3.pasm Thu May 29 13:12:58 2003 @@ -38,6 +38,9 @@ interpinfo I1, 4 print I1 print " active PMCs\n" + interpinfo I1, 6 + print I1 + print " total PMCs\n" end --- parrot/headers.c Mon Jan 13 18:05:14 2003 +++ parrot-leo/headers.c Wed May 28 16:26:30 2003 @@ -39,8 +39,7 @@ /* clear flags, set is_PMC_FLAG */ PObj_flags_SETTO(pmc, PObj_is_PMC_FLAG); ((PMC *)pmc)->data = NULL; - ((PMC *)pmc)->metadata = NULL; - ((PMC *)pmc)->synchronize = NULL; + ((PMC *)pmc)->pmc_ext = NULL; return pmc; } @@ -169,6 +168,24 @@ return get_free_pmc(interpreter, interpreter->arena_base->pmc_pool); } + +PMC_EXT * +new_pmc_ext(struct Parrot_Interp *interpreter) +{ + struct Small_Object_Pool *pool = interpreter->arena_base->pmc_ext_pool; + void *ptr = get_free_object(interpreter, pool); + memset(ptr, 0, sizeof(PMC_EXT)); + return ptr; +} + +void +add_pmc_ext(struct Parrot_Interp *interpreter, PMC *pmc) +{ + pmc->pmc_ext = new_pmc_ext(interpreter); + PObj_is_PMC_EXT_SET(pmc); +} + + STRING * new_string_header(struct Parrot_Interp *interpreter, UINTVAL flags) { @@ -308,6 +325,8 @@ /* Init the constant string header pool */ interpreter->arena_base->constant_string_header_pool = new_string_pool(interpreter, 1); + interpreter->arena_base->constant_string_header_pool->name = + "constant_string_header"; /* Init the buffer header pool @@ -317,13 +336,21 @@ * here for faster access in new_*_header */ interpreter->arena_base->buffer_header_pool = new_buffer_pool(interpreter); + interpreter->arena_base->buffer_header_pool->name = "buffer_header"; /* Init the string header pool */ interpreter->arena_base->string_header_pool = new_string_pool(interpreter, 0); + interpreter->arena_base->string_header_pool->name = "string_header"; /* Init the PMC header pool */ interpreter->arena_base->pmc_pool = new_pmc_pool(interpreter); + interpreter->arena_base->pmc_pool->name = "pmc"; + interpreter->arena_base->pmc_ext_pool = + new_small_object_pool(interpreter, sizeof(struct PMC_EXT), 1024); + interpreter->arena_base->pmc_ext_pool->more_objects = + alloc_objects; + interpreter->arena_base->pmc_ext_pool->name = "pmc_ext"; interpreter->arena_base->constant_pmc_pool = new_pmc_pool(interpreter); interpreter->arena_base->constant_pmc_pool->objects_per_alloc = CONSTANT_PMC_HEADERS_PER_ALLOC; @@ -336,8 +363,16 @@ struct Small_Object_Arena *cur_arena, *next; int i, j, start; - /* const/non const COW strings live in different pools * so in first pass - * * * * COW refcount is done, in 2. refcounting * in 3rd freeing */ + /* TODO: -lt make a command line option for cleaning up: + * - the last interpreter may just die + * - created interpreters shhould always get destroyed + */ + return; + /* const/non const COW strings life in different pools + * so in first pass + * COW refcount is done, in 2. refcounting + * in 3rd freeing + */ #ifdef GC_IS_MALLOC start = 0; #else @@ -372,7 +407,9 @@ if (i == 2 && pool) { for (cur_arena = pool->last_Arena; cur_arena;) { next = cur_arena->prev; +#if ! ARENA_DOD_FLAGS mem_sys_free(cur_arena->start_objects); +#endif mem_sys_free(cur_arena); cur_arena = next; } @@ -381,6 +418,16 @@ } } + pool = interpreter->arena_base->pmc_ext_pool; + for (cur_arena = pool->last_Arena; cur_arena;) { + next = cur_arena->prev; +#if ! ARENA_DOD_FLAGS + mem_sys_free(cur_arena->start_objects); +#endif + mem_sys_free(cur_arena); + cur_arena = next; + } + mem_sys_free(interpreter->arena_base->pmc_ext_pool); mem_sys_free(interpreter->arena_base->sized_header_pools); } --- parrot/include/parrot/headers.h Mon Dec 30 11:47:26 2002 +++ parrot-leo/include/parrot/headers.h Mon May 26 14:13:04 2003 @@ -23,8 +23,6 @@ /** Header Management Functions **/ /* pmc header small-object methods */ -void add_free_pmc(struct Parrot_Interp *interpreter, - struct Small_Object_Pool *pool, void *pmc); void *get_free_pmc(struct Parrot_Interp *interpreter, struct Small_Object_Pool *pool); void alloc_pmcs(struct Parrot_Interp *interpreter, @@ -46,6 +44,8 @@ struct Small_Object_Pool *make_bufferlike_pool(struct Parrot_Interp *interpreter, size_t unit_size); /* header creation functions */ PMC *new_pmc_header(struct Parrot_Interp *interpreter); +PMC_EXT *new_pmc_ext(struct Parrot_Interp *interpreter); +void add_pmc_ext(struct Parrot_Interp *interpreter, PMC *pmc); STRING *new_string_header(struct Parrot_Interp *interpreter, UINTVAL flags); Buffer *new_buffer_header(struct Parrot_Interp *interpreter); void *new_bufferlike_header(struct Parrot_Interp *interpreter, size_t size); --- parrot/include/parrot/parrot.h Wed May 14 10:06:39 2003 +++ parrot-leo/include/parrot/parrot.h Wed May 28 15:42:50 2003 @@ -177,7 +177,7 @@ * samples by reducing various numbers, and causing DOD and allocation runs * to occur more frequently. It does significantly reduce performance. */ #ifndef DISABLE_GC_DEBUG -# define DISABLE_GC_DEBUG 0 +# define DISABLE_GC_DEBUG 1 #endif /* Helpful internal macro for testing whether we are currently --- parrot/include/parrot/pobj.h Fri May 23 10:02:42 2003 +++ parrot-leo/include/parrot/pobj.h Wed May 28 15:31:13 2003 @@ -15,6 +15,11 @@ #include "parrot/config.h" +/* + * live, on_free_list, special_PMC are kept in the pools arenas + */ +#define ARENA_DOD_FLAGS 1 + typedef union UnionVal { INTVAL int_val; /* PMC unionval members */ FLOATVAL num_val; @@ -68,7 +73,11 @@ pobj_t obj; VTABLE *vtable; DPOINTER *data; - PMC *metadata; + struct PMC_EXT *pmc_ext; +}; + +struct PMC_EXT { + PMC *metadata; /* properties */ SYNC *synchronize; /* This flag determines the next PMC in the 'used' list during @@ -77,24 +86,26 @@ guaranteed to have the tail element's next_for_GC point to itself, which makes much of the logic and checks simpler. We then have to check for PMC->next_for_GC == PMC to find the end of list. */ - PMC *next_for_GC; /* Yeah, the GC data should be out of - band, but that makes things really - slow when actually marking things for - the GC runs. Unfortunately putting - this here makes marking things clear - for the GC pre-run slow as well, as - we need to touch all the PMC - structs. (Though we will for flag - setting anyway) We can potentially - make this a pointer to the real GC - stuff, which'd merit an extra - dereference when setting, but let us - memset the actual GC data in a big - block */ + PMC *next_for_GC; + + /* Yeah, the GC data should be out of + band, but that makes things really slow when actually marking + things for the GC runs. Unfortunately putting this here makes + marking things clear for the GC pre-run slow as well, as we need + to touch all the PMC structs. (Though we will for flag setting + anyway) We can potentially make this a pointer to the real GC + stuff, which'd merit an extra dereference when setting, but let + us memset the actual GC data in a big block + */ }; +typedef struct PMC_EXT PMC_EXT; + /* macro for accessing union data */ #define cache obj.u +#define metadata pmc_ext->metadata +#define next_for_GC pmc_ext->next_for_GC +#define synchronize pmc_ext->synchronize /* PObj flags */ typedef enum PObj_enum { @@ -117,7 +128,7 @@ PObj_is_string_FLAG = 1 << 8, /* PObj is a PMC */ PObj_is_PMC_FLAG = 1 << 9, - PObj_is_reserved1_FLAG = 1 << 10, + PObj_is_PMC_EXT_FLAG = 1 << 10, PObj_is_reserved2_FLAG = 1 << 11, /* Memory management FLAGs */ @@ -140,9 +151,9 @@ PObj_constant_FLAG = 1 << 17, /* Private flag for the GC system. Set if the PObj's in use as * far as the GC's concerned */ - PObj_live_FLAG = 1 << 18, + b_PObj_live_FLAG = 1 << 18, /* Mark the object as on the free list */ - PObj_on_free_list_FLAG = 1 << 19, + b_PObj_on_free_list_FLAG = 1 << 19, /* DOD/GC FLAGS */ @@ -172,9 +183,9 @@ * - is_buffer_ptr_FLAG * - custom_mark_FLAG */ - PObj_is_special_PMC_FLAG = 1 << 26, + b_PObj_is_special_PMC_FLAG = 1 << 26, - PObj_is_impatient_FLAG = 1 << 27 + b_PObj_is_impatient_FLAG = 1 << 27 } PObj_flags; @@ -184,6 +195,79 @@ * these macros */ +#if ARENA_DOD_FLAGS +/* + * these 4 flags are kept in one nibble + */ +# define d_PObj_live_FLAG 0x01 +# define d_PObj_on_free_list_FLAG 0x02 +# define d_PObj_is_special_PMC_FLAG 0x04 +# define d_PObj_is_impatient_FLAG 0x08 + +/* + * arenas are constant sized ~32 byte object size, ~128K objects + */ +# define ARENA_SIZE (32*1024*128) +# define ARENA_ALIGN ARENA_SIZE +# define ARENA_MASK (~ (ARENA_SIZE-1) ) +#if INTVAL_SIZE == 4 +# define ARENA_FLAG_SHIFT 3 +# define ARENA_FLAG_MASK 0x7 +# define ALL_LIVE_MASK 0x11111111 +# define ALL_FREE_MASK 0x22222222 +#elif INTVAL_SIZE == 8 +# define ARENA_FLAG_SHIFT 4 +# define ARENA_FLAG_MASK 0x0f +# define ALL_LIVE_MASK 0x1111111111111111 +# define ALL_FREE_MASK 0x2222222222222222 +#else +# error Unsupported INTVAL_SIZE +#endif +# define ARENA_OBJECTS(_pool) ( ARENA_SIZE / _pool->object_size ) +# define ARENA_FLAG_SIZE(_pool) \ + (4*sizeof(INTVAL) + sizeof(INTVAL) * \ + ((ARENA_OBJECTS(_pool) >> ARENA_FLAG_SHIFT )) ) + +# define GET_ARENA(o) \ + ((struct Small_Object_Arena *) (PTR2UINTVAL(o) & ARENA_MASK)) +# define GET_OBJ_N(arena, o) \ + ((PTR2UINTVAL(o) - PTR2UINTVAL((arena)->start_objects)) \ + / (arena)->object_size) + +# define DOD_flag_SET(flag, o) \ + do { \ + struct Small_Object_Arena *_arena = GET_ARENA(o); \ + size_t _n = GET_OBJ_N(_arena, o); \ + _arena->dod_flags[ _n >> ARENA_FLAG_SHIFT ] |= \ + ((d_PObj_ ## flag ## _FLAG << (( _n & ARENA_FLAG_MASK ) << 2))); \ + } \ + while (0) +# define DOD_flag_CLEAR(flag, o) \ + do { \ + struct Small_Object_Arena *_arena = GET_ARENA(o); \ + size_t _n = GET_OBJ_N(_arena, o); \ + _arena->dod_flags[ _n >> ARENA_FLAG_SHIFT ] &= \ + ~((d_PObj_ ## flag ## _FLAG << (( _n & ARENA_FLAG_MASK ) << 2))); \ + } \ + while (0) + +# define PObj_live_FLAG d_PObj_live_FLAG +# define PObj_on_free_list_FLAG d_PObj_on_free_list_FLAG +# define PObj_is_special_PMC_FLAG d_PObj_is_special_PMC_FLAG +# define PObj_is_impatient_FLAG d_PObj_is_impatient_FLAG + +#else + +# define PObj_live_FLAG b_PObj_live_FLAG +# define PObj_on_free_list_FLAG b_PObj_on_free_list_FLAG +# define PObj_is_special_PMC_FLAG b_PObj_is_special_PMC_FLAG +# define PObj_is_impatient_FLAG b_PObj_is_impatient_FLAG + +# define DOD_flag_TEST(flag, o) PObj_flag_TEST(flag, o) +# define DOD_flag_SET(flag, o) PObj_flag_SET(flag, o) +# define DOD_flag_CLEAR(flag, o) PObj_flag_CLEAR(flag, o) + +#endif #define PObj_get_FLAGS(o) ((o)->obj.flags) @@ -214,13 +298,13 @@ #define PObj_report_SET(o) PObj_flag_SET(report, o) #define PObj_report_CLEAR(o) PObj_flag_CLEAR(report, o) -#define PObj_on_free_list_TEST(o) PObj_flag_TEST(on_free_list, o) -#define PObj_on_free_list_SET(o) PObj_flag_SET(on_free_list, o) -#define PObj_on_free_list_CLEAR(o) PObj_flag_CLEAR(on_free_list, o) - -#define PObj_live_TEST(o) PObj_flag_TEST(live, o) -#define PObj_live_SET(o) PObj_flag_SET(live, o) -#define PObj_live_CLEAR(o) PObj_flag_CLEAR(live, o) +#define PObj_on_free_list_TEST(o) DOD_flag_TEST(on_free_list, o) +#define PObj_on_free_list_SET(o) DOD_flag_SET(on_free_list, o) +#define PObj_on_free_list_CLEAR(o) DOD_flag_CLEAR(on_free_list, o) + +#define PObj_live_TEST(o) DOD_flag_TEST(live, o) +#define PObj_live_SET(o) DOD_flag_SET(live, o) +#define PObj_live_CLEAR(o) DOD_flag_CLEAR(live, o) #define PObj_is_string_TEST(o) PObj_flag_TEST(is_string, o) #define PObj_is_string_SET(o) PObj_flag_SET(is_string, o) @@ -234,40 +318,43 @@ #define PObj_sysmem_SET(o) PObj_flag_SET(sysmem, o) #define PObj_sysmem_CLEAR(o) PObj_flag_CLEAR(sysmem, o) -#define PObj_is_impatient_TEST(o) PObj_flag_TEST(is_impatient, o) -#define PObj_is_impatient_SET(o) PObj_flag_SET(is_impatient, o) -#define PObj_is_impatient_CLEAR(o) PObj_flag_CLEAR(is_impatient, o) +#define PObj_is_impatient_TEST(o) DOD_flag_TEST(is_impatient, o) +#define PObj_is_impatient_SET(o) DOD_flag_SET(is_impatient, o) +#define PObj_is_impatient_CLEAR(o) DOD_flag_CLEAR(is_impatient, o) #define PObj_special_SET(flag, o) do { \ PObj_flag_SET(flag, o); \ - PObj_flag_SET(is_special_PMC, o); \ + DOD_flag_SET(is_special_PMC, o); \ } while(0) #define PObj_special_CLEAR(flag, o) do { \ PObj_flag_CLEAR(flag, o); \ - if ((PObj_get_FLAGS(o) & (PObj_active_destroy_FLAG | PObj_is_PMC_ptr_FLAG | \ + if ((PObj_get_FLAGS(o) & \ + (PObj_active_destroy_FLAG | PObj_is_PMC_ptr_FLAG | \ PObj_is_buffer_ptr_FLAG)) || \ - (PObj_is_PMC_TEST(o) && ((struct PMC*)(o))->metadata)) \ - PObj_flag_SET(is_special_PMC, o); \ + (PObj_is_PMC_TEST(o) && \ + ((struct PMC*)(o))->pmc_ext && \ + ((struct PMC*)(o))->metadata)) \ + DOD_flag_SET(is_special_PMC, o); \ else \ - PObj_flag_CLEAR(is_special_PMC, o); \ + DOD_flag_CLEAR(is_special_PMC, o); \ } while (0) -#define PObj_is_special_PMC_TEST(o) PObj_flag_TEST(is_special_PMC, o) -#define PObj_is_special_PMC_SET(o) PObj_flag_SET(is_special_PMC, o) +#define PObj_is_special_PMC_TEST(o) DOD_flag_TEST(is_special_PMC, o) +#define PObj_is_special_PMC_SET(o) DOD_flag_SET(is_special_PMC, o) #define PObj_is_buffer_ptr_SET(o) PObj_special_SET(is_buffer_ptr, o) #define PObj_is_buffer_ptr_CLEAR(o) PObj_special_CLEAR(is_buffer_ptr, o) #define PObj_custom_mark_SET(o) PObj_special_SET(custom_mark, o) #define PObj_custom_mark_CLEAR(o) PObj_special_CLEAR(custom_mark, o) +#define PObj_custom_mark_TEST(o) PObj_flag_TEST(custom_mark, o) #define PObj_active_destroy_SET(o) PObj_flag_SET(active_destroy, o) #define PObj_active_destroy_TEST(o) PObj_flag_TEST(active_destroy, o) #define PObj_active_destroy_CLEAR(o) PObj_flag_CLEAR(active_destroy, o) #define PObj_is_PMC_TEST(o) PObj_flag_TEST(is_PMC, o) -#define PObj_is_SPMC_TEST(o) PObj_flag_TEST(is_SPMC, o) -#define PObj_is_any_PMC_TESTALL(o) (PObj_get_FLAGS(o) & \ - (PObj_is_PMC_FLAG|PObj_is_SPMC_FLAG)) +#define PObj_is_PMC_EXT_TEST(o) PObj_flag_TEST(is_PMC_EXT, o) +#define PObj_is_PMC_EXT_SET(o) PObj_special_SET(is_PMC_EXT, o) /* some combinations */ @@ -291,7 +378,7 @@ (PObj_live_FLAG | PObj_on_free_list_FLAG)) #define PObj_is_movable_TESTALL(o) (!(PObj_get_FLAGS(o) & \ - (PObj_immobile_FLAG | PObj_on_free_list_FLAG | \ + (PObj_immobile_FLAG | \ PObj_constant_FLAG | PObj_external_FLAG))) #define PObj_custom_mark_destroy_SETALL(o) do { \ --- parrot/include/parrot/resources.h Sat Jan 11 12:01:03 2003 +++ parrot-leo/include/parrot/resources.h Sat May 24 10:17:10 2003 @@ -56,6 +56,7 @@ struct Memory_Pool *constant_string_pool; struct Small_Object_Pool *string_header_pool; struct Small_Object_Pool *pmc_pool; + struct Small_Object_Pool *pmc_ext_pool; struct Small_Object_Pool *constant_pmc_pool; struct Small_Object_Pool *buffer_header_pool; struct Small_Object_Pool *constant_string_header_pool; --- parrot/include/parrot/smallobject.h Sat Dec 21 11:08:08 2002 +++ parrot-leo/include/parrot/smallobject.h Wed May 28 10:37:44 2003 @@ -6,6 +6,13 @@ struct Small_Object_Arena { size_t used; size_t total_objects; +#if ARENA_DOD_FLAGS + size_t object_size; /* size in bytes of an individual pool item */ + UINTVAL * dod_flags; + struct Small_Object_Pool * pool; + size_t live_objects; + void *free_list; +#endif struct Small_Object_Arena *prev; struct Small_Object_Arena *next; void *start_objects; @@ -18,12 +25,22 @@ size_t objects_per_alloc; size_t total_objects; size_t num_free_objects; /* number of resources in the free pool */ + int skip; size_t replenish_level; +#if ARENA_DOD_FLAGS + struct Small_Object_Arena *free_arena; +#else void *free_list; +#endif UINTVAL align_1; /* alignment (must be power of 2) minus one */ /* adds a free object to the pool's free list */ +#if ARENA_DOD_FLAGS + void (*add_free_object)(struct Parrot_Interp *, + struct Small_Object_Arena *, void *); +#else void (*add_free_object)(struct Parrot_Interp *, struct Small_Object_Pool *, void *); +#endif /* gets and removes a free object from the pool's free list */ void *(*get_free_object)(struct Parrot_Interp *, struct Small_Object_Pool *); @@ -36,7 +53,7 @@ void *mem_pool; size_t start_arena_memory; size_t end_arena_memory; - STRING* name; + const char *name; }; INTVAL contained_in_pool(struct Parrot_Interp *, @@ -52,8 +69,13 @@ void more_non_traceable_objects(struct Parrot_Interp *interpreter, struct Small_Object_Pool *pool); +#if ARENA_DOD_FLAGS +void add_free_object(struct Parrot_Interp *, + struct Small_Object_Arena *, void *); +#else void add_free_object(struct Parrot_Interp *, struct Small_Object_Pool *, void *); +#endif void *get_free_object(struct Parrot_Interp *, struct Small_Object_Pool *); --- parrot/jit_debug.c Thu May 1 10:06:05 2003 +++ parrot-leo/jit_debug.c Sat May 24 11:40:44 2003 @@ -148,8 +148,10 @@ i + 3, BIT_OFFSET(PMC, vtable), BIT_SIZE(void*)); fprintf(stabs, "data:(0,14),%d,%d;", BIT_OFFSET(PMC, data), BIT_SIZE(void*)); +#if 0 fprintf(stabs, "metadata:*(0,%d),%d,%d;", i, BIT_OFFSET(PMC, metadata), BIT_SIZE(void*)); +#endif fprintf(stabs, ";\""); fprintf(stabs, "," N_LSYM ",0,0,0\n"); --- parrot/pmc.c Mon May 19 15:07:49 2003 +++ parrot-leo/pmc.c Wed May 28 14:17:57 2003 @@ -73,8 +73,22 @@ PMC * pmc_new_noinit(struct Parrot_Interp *interpreter, INTVAL base_type) { - return get_new_pmc_header(interpreter, base_type, + PMC *pmc = get_new_pmc_header(interpreter, base_type, interpreter->arena_base->pmc_pool); + switch (base_type) { + case enum_class_PerlInt: + case enum_class_PerlNum: + case enum_class_PerlString: + case enum_class_PerlUndef: + break; + default: + /* TODO optimize this, mainly only aggregates need + * the extra header part + */ + add_pmc_ext(interpreter, pmc); + break; + } + return pmc; } /*=for api pmc constant_pmc_new_noinit --- parrot/resources.c Mon Feb 10 17:08:48 2003 +++ parrot-leo/resources.c Mon May 26 09:46:19 2003 @@ -213,7 +213,7 @@ for (i = 0; i < cur_buffer_arena->used; i++) { /* ! (immobile | on_free_list | constant | external) */ - if (b->bufstart && PObj_is_movable_TESTALL(b)) { + if (b->buflen && PObj_is_movable_TESTALL(b)) { struct Buffer_Tail *tail = (struct Buffer_Tail *)((char *)b->bufstart + b->buflen); --- parrot/smallobject.c Mon Jan 13 18:05:14 2003 +++ parrot-leo/smallobject.c Wed May 28 15:06:16 2003 @@ -13,6 +13,7 @@ */ #include "parrot/parrot.h" +#include <assert.h> #define GC_DEBUG_REPLENISH_LEVEL_FACTOR 0.0 #define GC_DEBUG_UNITS_PER_ALLOC_GROWTH_FACTOR 1 @@ -33,24 +34,39 @@ if (0 <= ptr_diff && ptr_diff < - (ptrdiff_t)(arena->total_objects * pool->object_size) + (ptrdiff_t)(arena->used * pool->object_size) && ptr_diff % pool->object_size == 0) return 1; } return 0; } +static int find_free_list(struct Small_Object_Pool *pool); + /* We're out of traceable objects. Try a DOD, then get some more if needed */ void more_traceable_objects(struct Parrot_Interp *interpreter, struct Small_Object_Pool *pool) { + if (pool->skip) + pool->skip = 0; + else { Parrot_do_dod_run(interpreter); + if (pool->num_free_objects <= pool->replenish_level) + pool->skip = 1; + } + /* requires that num_free_objects be updated in Parrot_do_dod_run. If dod * is disabled, then we must check the free list directly. */ - if (!pool->free_list || pool->num_free_objects <= pool->replenish_level) { +#if ARENA_DOD_FLAGS + if (!find_free_list(pool)) { + (*pool->alloc_objects) (interpreter, pool); + } +#else + if (!pool->free_list) { (*pool->alloc_objects) (interpreter, pool); } +#endif } /* We're out of non-traceable objects. Get some more */ @@ -61,6 +77,59 @@ (*pool->alloc_objects) (interpreter, pool); } +#if ARENA_DOD_FLAGS +static int +find_free_list(struct Small_Object_Pool *pool) +{ + struct Small_Object_Arena *arena; + + for (arena = pool->last_Arena; arena; arena = arena->prev) { + if (arena->free_list) { + pool->free_arena = arena; + return 1; + } + } + return 0; +} + +void +add_free_object(struct Parrot_Interp *interpreter, + struct Small_Object_Arena *arena, void *to_add) +{ + *(void **)to_add = arena->free_list; + arena->free_list = to_add; +} + +/* Get a new object from the free pool and return it */ +void * +get_free_object(struct Parrot_Interp *interpreter, + struct Small_Object_Pool *pool) +{ + void *ptr; + struct Small_Object_Arena *arena; + size_t n; + + /* if we don't have any objects */ + if (!pool->free_arena) + (*pool->alloc_objects) (interpreter, pool); + if (!pool->free_arena->free_list) + if (!find_free_list(pool)) + (*pool->more_objects) (interpreter, pool); + + arena = pool->free_arena; + ptr = arena->free_list; + n = GET_OBJ_N(arena, ptr); + arena->dod_flags[ n >> ARENA_FLAG_SHIFT ] &= + ~((PObj_on_free_list_FLAG << (( n & ARENA_FLAG_MASK ) << 2))); + arena->free_list = *(void **)ptr; +#if ! DISABLE_GC_DEBUG + if (GC_DEBUG(interpreter)) + ((Buffer*)ptr)->version++; +#endif + return ptr; +} + +#else /* Add an unused object back to the free pool for later reuse */ void add_free_object(struct Parrot_Interp *interpreter, @@ -69,7 +138,6 @@ PObj_flags_SETTO((PObj *)to_add, PObj_on_free_list_FLAG); *(void **)to_add = pool->free_list; pool->free_list = to_add; - pool->num_free_objects++; } /* Get a new object from the free pool and return it */ @@ -85,13 +153,56 @@ ptr = pool->free_list; pool->free_list = *(void **)ptr; - pool->num_free_objects--; #if ! DISABLE_GC_DEBUG if (GC_DEBUG(interpreter)) ((Buffer*)ptr)->version++; #endif return ptr; } +#endif + + +static void +add_to_free_list(struct Parrot_Interp *interpreter, + struct Small_Object_Pool *pool, + struct Small_Object_Arena *arena, + UINTVAL start, + UINTVAL end) +{ +#if ARENA_DOD_FLAGS + UINTVAL *dod_flags; +#endif + UINTVAL i; + void *object; + + pool->total_objects += end - start; + arena->used = end; + + /* Move all the new objects into the free list */ + object = (void *)((char *)arena->start_objects + + start * pool->object_size); +#if ARENA_DOD_FLAGS + pool->replenish_level = + (size_t)(pool->total_objects * REPLENISH_LEVEL_FACTOR); + dod_flags = arena->dod_flags + (start >> ARENA_FLAG_SHIFT); +#endif + for (i = start; i < end; i++) { +#if ARENA_DOD_FLAGS + if (! (i & ARENA_FLAG_MASK)) { + *dod_flags = ALL_FREE_MASK; + ++dod_flags; + } + pool->add_free_object (interpreter, arena, object); +#else + pool->add_free_object (interpreter, pool, object); +#endif + object = (void *)((char *)object + pool->object_size); + } +#if ARENA_DOD_FLAGS + /* set last */ + *dod_flags = ALL_FREE_MASK; +#endif +} /* We have no more headers on the free header pool. Go allocate more * and put them on */ @@ -100,14 +211,50 @@ struct Small_Object_Pool *pool) { struct Small_Object_Arena *new_arena; - void *object; - UINTVAL i; - size_t size = pool->object_size * pool->objects_per_alloc; - - new_arena = mem_sys_allocate(sizeof(struct Small_Object_Arena)); + size_t size; + UINTVAL start, end; /* Setup memory for the new objects */ +#if ARENA_DOD_FLAGS + size_t offset; + + /* check old arena first */ + if (pool->last_Arena && + pool->last_Arena->used < pool->last_Arena->total_objects) { + start = pool->last_Arena->used; + end = start << 2; + if (end > pool->last_Arena->total_objects) + end = pool->last_Arena->total_objects; + add_to_free_list(interpreter, pool, pool->last_Arena, start, end); + return; + } + + size = ARENA_SIZE; + new_arena = Parrot_memalign(ARENA_ALIGN, size); + /* offset in bytes of whole Objects */ + offset = ( 1 + sizeof(struct Small_Object_Arena) / pool->object_size) * + pool->object_size; + new_arena->start_objects = (char *)new_arena + offset; + size -= offset; + pool->objects_per_alloc = size / pool->object_size; + new_arena->object_size = pool->object_size; + /* on Linux the mmap()ed memory is cleared + * as the size is big, we get cleared mem + * + * XXX to be sure we need tests: + * at which size do we get cleared mem + * - for memalign + * - for malloc + */ + /* memset(new_arena->start_objects, 0, size); */ + + new_arena->dod_flags = mem_sys_allocate(ARENA_FLAG_SIZE(pool)); + new_arena->pool = pool; +#else + new_arena = mem_sys_allocate(sizeof(struct Small_Object_Arena)); + size = pool->object_size * pool->objects_per_alloc; new_arena->start_objects = mem_sys_allocate_zeroed(size); +#endif /* Maintain the *_arena_memory invariant for stack walking code. Set it * regardless if we're the first pool to be added. */ @@ -120,7 +267,22 @@ pool->end_arena_memory = (size_t)new_arena->start_objects + size; /* Hook up the new object block into the object pool */ - new_arena->used = pool->objects_per_alloc; +#if ARENA_DOD_FLAGS + /* not the first one - put all on free list */ + if (pool->last_Arena) { + start = 0; + end = pool->objects_per_alloc; + } + else { + /* first arena, start with x objects */ + start = 0; + end = 1024; + } + pool->free_arena = new_arena; +#else + start = 0; + end = pool->objects_per_alloc; +#endif new_arena->total_objects = pool->objects_per_alloc; new_arena->next = NULL; new_arena->prev = pool->last_Arena; @@ -130,14 +292,9 @@ pool->last_Arena = new_arena; interpreter->header_allocs_since_last_collect++; - /* Move all the new objects into the free list */ - object = new_arena->start_objects; - for (i = 0; i < pool->objects_per_alloc; i++) { - add_free_object (interpreter, pool, object); - object = (void *)((char *)object + pool->object_size); - } - pool->total_objects += pool->objects_per_alloc; + add_to_free_list(interpreter, pool, new_arena, start, end); +#if ! ARENA_DOD_FLAGS /* Allocate more next time */ if (GC_DEBUG(interpreter)) { pool->objects_per_alloc *= GC_DEBUG_UNITS_PER_ALLOC_GROWTH_FACTOR; @@ -156,6 +313,7 @@ if (size > POOL_MAX_BYTES) { pool->objects_per_alloc = POOL_MAX_BYTES / pool->object_size; } +#endif } struct Small_Object_Pool *