implemented automatic generational processing based on threshold in garbage collection
This commit is contained in:
parent
752b1332c9
commit
7d70005984
@ -405,9 +405,17 @@ struct hawk_rtx_t
|
||||
|
||||
struct
|
||||
{
|
||||
hawk_gch_t g[HAWK_GC_NUM_GENS]; /* lists of values under gc management */
|
||||
hawk_oow_t nv[HAWK_GC_NUM_GENS]; /* number of values in each list */
|
||||
hawk_oow_t nc[HAWK_GC_NUM_GENS]; /* number of collections performed for each generation */
|
||||
/* lists of values under gc management */
|
||||
hawk_gch_t g[HAWK_GC_NUM_GENS];
|
||||
|
||||
/*
|
||||
* ncolls[0] - number of allocation attempt since the last gc
|
||||
* ncolls[N] - nubmer of collections performed for generation N - 1.
|
||||
*/
|
||||
hawk_oow_t ncolls[HAWK_GC_NUM_GENS + 1];
|
||||
|
||||
/* threshold to trigger generational collection. */
|
||||
hawk_oow_t threshold[HAWK_GC_NUM_GENS];
|
||||
} gc;
|
||||
|
||||
hawk_nde_blk_t* active_block;
|
||||
|
@ -3005,8 +3005,11 @@ HAWK_EXPORT void hawk_rtx_refdownval_nofree (
|
||||
);
|
||||
|
||||
|
||||
#define HAWK_RTX_GC_GEN_FULL (HAWK_TYPE_MAX(int))
|
||||
#define HAWK_RTX_GC_GEN_AUTO (-1)
|
||||
HAWK_EXPORT void hawk_rtx_gc (
|
||||
hawk_rtx_t* rtx
|
||||
hawk_rtx_t* rtx,
|
||||
int gen
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -1039,10 +1039,13 @@ static int init_rtx (hawk_rtx_t* rtx, hawk_t* awk, hawk_rio_cbs_t* rio)
|
||||
rtx->gc.g[i].gc_prev = &rtx->gc.g[i];
|
||||
|
||||
/* initialize some counters */
|
||||
rtx->gc.nv[i] = 0;
|
||||
rtx->gc.nc[i] = 0;
|
||||
rtx->gc.ncolls[i] = 0;
|
||||
rtx->gc.threshold[i] = (HAWK_COUNTOF(rtx->gc.g) - i) * 3;
|
||||
if (i == 0 && rtx->gc.threshold[i] < 100) rtx->gc.threshold[i] = 100; /* minimum threshold for gen 0 is 100 */
|
||||
}
|
||||
|
||||
rtx->gc.ncolls[i] = 0; /* ncolls is larger than other elements by 1 in size */
|
||||
|
||||
rtx->inrec.buf_pos = 0;
|
||||
rtx->inrec.buf_len = 0;
|
||||
rtx->inrec.flds = HAWK_NULL;
|
||||
@ -1261,7 +1264,7 @@ static void fini_rtx (hawk_rtx_t* rtx, int fini_globals)
|
||||
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
/* collect garbage after having released global variables and named global variables */
|
||||
hawk_rtx_gc (rtx);
|
||||
hawk_rtx_gc (rtx, HAWK_RTX_GC_GEN_FULL);
|
||||
#endif
|
||||
|
||||
/* destroy values in free list */
|
||||
@ -1682,7 +1685,7 @@ hawk_val_t* hawk_rtx_execwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* args[]
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
/* i assume this function is a usual hawk program starter.
|
||||
* call garbage collection after a whole program finishes */
|
||||
hawk_rtx_gc (rtx);
|
||||
hawk_rtx_gc (rtx, HAWK_RTX_GC_GEN_FULL);
|
||||
#endif
|
||||
|
||||
return v;
|
||||
@ -1699,7 +1702,7 @@ hawk_val_t* hawk_rtx_execwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* args[]
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
/* i assume this function is a usual hawk program starter.
|
||||
* call garbage collection after a whole program finishes */
|
||||
hawk_rtx_gc (rtx);
|
||||
hawk_rtx_gc (rtx, HAWK_RTX_GC_GEN_FULL);
|
||||
#endif
|
||||
|
||||
return v;
|
||||
|
174
hawk/lib/val.c
174
hawk/lib/val.c
@ -111,16 +111,6 @@ BEGIN {
|
||||
#define GCH_MOVED HAWK_TYPE_MAX(hawk_uintptr_t)
|
||||
#define GCH_UNREACHABLE (GCH_MOVED - 1)
|
||||
|
||||
static hawk_val_t* gc_calloc (hawk_rtx_t* rtx, hawk_oow_t size)
|
||||
{
|
||||
hawk_gch_t* gch;
|
||||
|
||||
gch = (hawk_gch_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*gch) + size);
|
||||
if (HAWK_UNLIKELY(!gch)) return HAWK_NULL;
|
||||
|
||||
return hawk_gch_to_val(gch);
|
||||
}
|
||||
|
||||
static HAWK_INLINE void gc_chain_gch (hawk_gch_t* list, hawk_gch_t* gch)
|
||||
{
|
||||
gch->gc_next = list;
|
||||
@ -204,6 +194,7 @@ static void gc_trace_refs (hawk_gch_t* list)
|
||||
static void gc_dump_refs (hawk_rtx_t* rtx, hawk_gch_t* list)
|
||||
{
|
||||
hawk_gch_t* gch;
|
||||
hawk_oow_t count = 0;
|
||||
|
||||
gch = list->gc_next;
|
||||
while (gch != list)
|
||||
@ -211,7 +202,7 @@ static void gc_dump_refs (hawk_rtx_t* rtx, hawk_gch_t* list)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] GCH %p gc_refs %d\n", gch, (int)gch->gc_refs);
|
||||
gch = gch->gc_next;
|
||||
}
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] all_count => %d\n", (int)rtx->gc.nv[0]);
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] dumped %ju values\n", count);
|
||||
}
|
||||
|
||||
static void gc_move_reachables (hawk_gch_t* list, hawk_gch_t* reachable_list)
|
||||
@ -264,6 +255,11 @@ static void gc_move_reachables (hawk_gch_t* list, hawk_gch_t* reachable_list)
|
||||
}
|
||||
}
|
||||
|
||||
static HAWK_INLINE void gc_free_val (hawk_rtx_t* rtx, hawk_val_t* v)
|
||||
{
|
||||
hawk_rtx_freemem (rtx, hawk_val_to_gch(v));
|
||||
}
|
||||
|
||||
static void gc_free_unreachables (hawk_rtx_t* rtx, hawk_gch_t* list)
|
||||
{
|
||||
hawk_gch_t* gch;
|
||||
@ -299,47 +295,59 @@ static void gc_free_unreachables (hawk_rtx_t* rtx, hawk_gch_t* list)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] FREEING UNREACHABLE GCH %p gc_refs %zu v_refs %zu\n", gch, gch->gc_refs, hawk_gch_to_val(gch)->v_refs);
|
||||
#endif
|
||||
/* do what hawk_rtx_freeval() would do without HAWK_RTX_FREEVAL_GC_PRESERVE */
|
||||
rtx->gc.nv[0]--;
|
||||
gc_unchain_gch (gch);
|
||||
hawk_rtx_freemem (rtx, gch);
|
||||
gc_free_val (rtx, hawk_gch_to_val(gch));
|
||||
}
|
||||
}
|
||||
|
||||
static void gc_collect_garbage_in_generation (hawk_rtx_t* rtx, int gen)
|
||||
static HAWK_INLINE void gc_collect_garbage_in_generation (hawk_rtx_t* rtx, int gen)
|
||||
{
|
||||
hawk_oow_t i, newgen;
|
||||
hawk_gch_t reachable;
|
||||
|
||||
#if defined(DEBUG_GC)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] **started**\n");
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] **started - gen %d**\n", gen);
|
||||
#endif
|
||||
|
||||
newgen = (gen < HAWK_COUNTOF(rtx->gc.g) - 1)? (gen + 1): gen;
|
||||
for (i = 0; i < gen; i++) gc_move_all_gchs (&rtx->gc.g[i], &rtx->gc.g[gen]);
|
||||
for (i = 0; i < gen; i++)
|
||||
{
|
||||
gc_move_all_gchs (&rtx->gc.g[i], &rtx->gc.g[gen]);
|
||||
}
|
||||
|
||||
gc_trace_refs (&rtx->gc.g[gen]);
|
||||
if (rtx->gc.g[gen].gc_next != &rtx->gc.g[gen])
|
||||
{
|
||||
hawk_gch_t reachable;
|
||||
|
||||
reachable.gc_prev = &reachable;
|
||||
reachable.gc_next = &reachable;
|
||||
gc_move_reachables (&rtx->gc.g[gen], &reachable);
|
||||
gc_trace_refs (&rtx->gc.g[gen]);
|
||||
|
||||
/* only unreachables are left in rtx->gc.g[0] */
|
||||
#if defined(DEBUG_GC)
|
||||
/*gc_dump_refs (rtx, &rtx->gc.g[0]);*/
|
||||
#endif
|
||||
gc_free_unreachables (rtx, &rtx->gc.g[gen]);
|
||||
HAWK_ASSERT (rtx->gc.g[gen].gc_next == &rtx->gc.g[gen]);
|
||||
reachable.gc_prev = &reachable;
|
||||
reachable.gc_next = &reachable;
|
||||
gc_move_reachables (&rtx->gc.g[gen], &reachable);
|
||||
|
||||
/* move all reachables back to the main list */
|
||||
gc_move_all_gchs (&reachable, &rtx->gc.g[newgen]);
|
||||
/* only unreachables are left in rtx->gc.g[0] */
|
||||
#if defined(DEBUG_GC)
|
||||
/*gc_dump_refs (rtx, &rtx->gc.g[0]);*/
|
||||
#endif
|
||||
gc_free_unreachables (rtx, &rtx->gc.g[gen]);
|
||||
HAWK_ASSERT (rtx->gc.g[gen].gc_next == &rtx->gc.g[gen]);
|
||||
|
||||
/* move all reachables back to the main list */
|
||||
gc_move_all_gchs (&reachable, &rtx->gc.g[newgen]);
|
||||
}
|
||||
|
||||
/* [NOTE] ncolls is greater than other elements by 1 in size.
|
||||
* i store the number of collections for gen 0 in ncolls[1].
|
||||
* so i can avoid some comparison when doing this */
|
||||
rtx->gc.ncolls[gen + 1]++; /* number of collections done for gen */
|
||||
rtx->gc.ncolls[gen] = 0; /* reset the number of collections of the previous generation */
|
||||
rtx->gc.ncolls[0] = 0; /* reset the number of allocations since last gc. this line is redundant if gen is 0. */
|
||||
|
||||
#if defined(DEBUG_GC)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] **ended**\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void gc_collect_garbage (hawk_rtx_t* rtx)
|
||||
static HAWK_INLINE int gc_collect_garbage_auto (hawk_rtx_t* rtx)
|
||||
{
|
||||
hawk_oow_t i;
|
||||
|
||||
@ -347,52 +355,56 @@ static void gc_collect_garbage (hawk_rtx_t* rtx)
|
||||
while (i > 1)
|
||||
{
|
||||
--i;
|
||||
if (rtx->gc.nc[i - 1] > rtx->gc.t[i])
|
||||
if (rtx->gc.ncolls[i] >= rtx->gc.threshold[i])
|
||||
{
|
||||
gc_collect_garbage_in_generation(rtx, i);
|
||||
rtx->gc.nc[i]++;
|
||||
if (i > 0) rtx->gc.nc[i - 1] = 0;
|
||||
return;
|
||||
gc_collect_garbage_in_generation (rtx, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
gc_collect_garbage_in_generation(rtx, 0);
|
||||
rtx->gc.nc[0]++;
|
||||
gc_collect_garbage_in_generation (rtx, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void hawk_rtx_gc (hawk_rtx_t* rtx)
|
||||
void hawk_rtx_gc (hawk_rtx_t* rtx, int gen)
|
||||
{
|
||||
#if 0
|
||||
hawk_gch_t reachable;
|
||||
|
||||
#if defined(DEBUG_GC)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] **started**\n");
|
||||
#endif
|
||||
gc_trace_refs (&rtx->gc.g[0]);
|
||||
|
||||
reachable.gc_prev = &reachable;
|
||||
reachable.gc_next = &reachable;
|
||||
gc_move_reachables (&rtx->gc.g[0], &reachable);
|
||||
|
||||
/* only unreachables are left in rtx->gc.g[0] */
|
||||
#if defined(DEBUG_GC)
|
||||
/*gc_dump_refs (rtx, &rtx->gc.g[0]);*/
|
||||
#endif
|
||||
gc_free_unreachables (rtx, &rtx->gc.g[0]);
|
||||
HAWK_ASSERT (rtx->gc.g[0].gc_next == &rtx->gc.g[0]);
|
||||
|
||||
/* move all reachables back to the main list */
|
||||
gc_move_all_gchs (&reachable, &rtx->gc.g[0]);
|
||||
|
||||
#if defined(DEBUG_GC)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] **ended**\n");
|
||||
#endif
|
||||
if (gen < 0)
|
||||
{
|
||||
gc_collect_garbage_auto (rtx);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gen >= HAWK_COUNTOF(rtx->gc.g)) gen = HAWK_COUNTOF(rtx->gc.g) - 1;
|
||||
gc_collect_garbage_in_generation (rtx, gen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
gc_collect_garbage_in_generation (rtx, HAWK_COUNTOF(rtx->gc.g) - 1); /* full garbage collection */
|
||||
#endif
|
||||
static HAWK_INLINE hawk_val_t* gc_calloc_val (hawk_rtx_t* rtx, hawk_oow_t size)
|
||||
{
|
||||
hawk_gch_t* gch;
|
||||
int gc_gen = 0;
|
||||
|
||||
if (HAWK_UNLIKELY(rtx->gc.ncolls[0] >= rtx->gc.threshold[0]))
|
||||
{
|
||||
/* invoke generational garbage collection */
|
||||
gc_gen = gc_collect_garbage_auto(rtx);
|
||||
}
|
||||
|
||||
gch = (hawk_gch_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*gch) + size);
|
||||
if (HAWK_UNLIKELY(!gch))
|
||||
{
|
||||
if (gc_gen < HAWK_COUNTOF(rtx->gc.g) - 1)
|
||||
{
|
||||
/* perform full gc if full gc has not been triggerred at the beginning of this function */
|
||||
hawk_rtx_gc (rtx, HAWK_COUNTOF(rtx->gc.g) - 1);
|
||||
}
|
||||
gch = (hawk_gch_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*gch) + size);
|
||||
if (HAWK_UNLIKELY(!gch)) return HAWK_NULL;
|
||||
}
|
||||
|
||||
rtx->gc.ncolls[0]++; /* increment of the number of allocation attempt */
|
||||
return hawk_gch_to_val(gch);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -742,7 +754,6 @@ hawk_val_t* hawk_rtx_makenstrvalwithbcs (hawk_rtx_t* rtx, const hawk_bcs_t* str)
|
||||
return hawk_rtx_makenstrvalwithbchars(rtx, str->ptr, str->len);
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
|
||||
@ -931,11 +942,14 @@ hawk_val_t* hawk_rtx_makemapval (hawk_rtx_t* rtx)
|
||||
HAWK_MAP_HASHER_DEFAULT
|
||||
#endif
|
||||
};
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
int retried = 0;
|
||||
#endif
|
||||
hawk_val_map_t* val;
|
||||
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
hawk_rtx_gc(rtx);
|
||||
val = (hawk_val_map_t*)gc_calloc(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx));
|
||||
retry:
|
||||
val = (hawk_val_map_t*)gc_calloc_val(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx));
|
||||
#else
|
||||
val = (hawk_val_map_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx));
|
||||
#endif
|
||||
@ -948,9 +962,21 @@ hawk_rtx_gc(rtx);
|
||||
val->v_gc = 0;
|
||||
val->map = (hawk_map_t*)(val + 1);
|
||||
|
||||
if (hawk_map_init(val->map, hawk_rtx_getgem(rtx), 256, 70, HAWK_SIZEOF(hawk_ooch_t), 1) <= -1)
|
||||
if (HAWK_UNLIKELY(hawk_map_init(val->map, hawk_rtx_getgem(rtx), 256, 70, HAWK_SIZEOF(hawk_ooch_t), 1) <= -1))
|
||||
{
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
gc_free_val (rtx, (hawk_val_t*)val);
|
||||
if (HAWK_LIKELY(!retried))
|
||||
{
|
||||
/* this map involves non-gc allocatinon, which happens outside gc_calloc_val().
|
||||
* reattempt to allocate after full gc like gc_calloc_val() */
|
||||
hawk_rtx_gc (rtx, HAWK_COUNTOF(rtx->gc.g) - 1);
|
||||
retried = 1;
|
||||
goto retry;
|
||||
}
|
||||
#else
|
||||
hawk_rtx_freemem (rtx, val);
|
||||
#endif
|
||||
return HAWK_NULL;
|
||||
}
|
||||
*(hawk_rtx_t**)hawk_map_getxtn(val->map) = rtx;
|
||||
@ -958,7 +984,6 @@ hawk_rtx_gc(rtx);
|
||||
|
||||
#if defined(HAWK_ENABLE_GC)
|
||||
gc_chain_val (&rtx->gc.g[0], (hawk_val_t*)val);
|
||||
rtx->gc.nv[0]++;
|
||||
val->v_gc = 1;
|
||||
#if defined(DEBUG_GC)
|
||||
hawk_logbfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] MADE GCH %p VAL %p\n", hawk_val_to_gch(val), val);
|
||||
@ -1282,9 +1307,8 @@ void hawk_rtx_freeval (hawk_rtx_t* rtx, hawk_val_t* val, int flags)
|
||||
hawk_map_fini (((hawk_val_map_t*)val)->map);
|
||||
if (!(flags & HAWK_RTX_FREEVAL_GC_PRESERVE))
|
||||
{
|
||||
rtx->gc.nv[0]--;
|
||||
gc_unchain_val (val);
|
||||
hawk_rtx_freemem (rtx, hawk_val_to_gch(val));
|
||||
gc_free_val (rtx, val);
|
||||
}
|
||||
#else
|
||||
hawk_map_fini (((hawk_val_map_t*)val)->map);
|
||||
|
Loading…
Reference in New Issue
Block a user