From 7d70005984de8326a4934d8faa676dea25560f76 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sat, 28 Mar 2020 06:45:36 +0000 Subject: [PATCH] implemented automatic generational processing based on threshold in garbage collection --- hawk/lib/hawk-prv.h | 14 +++- hawk/lib/hawk.h | 5 +- hawk/lib/run.c | 13 ++-- hawk/lib/val.c | 174 +++++++++++++++++++++++++------------------- 4 files changed, 122 insertions(+), 84 deletions(-) diff --git a/hawk/lib/hawk-prv.h b/hawk/lib/hawk-prv.h index acce2ca1..9a4de8ca 100644 --- a/hawk/lib/hawk-prv.h +++ b/hawk/lib/hawk-prv.h @@ -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; diff --git a/hawk/lib/hawk.h b/hawk/lib/hawk.h index 489d0f16..f96e3a20 100644 --- a/hawk/lib/hawk.h +++ b/hawk/lib/hawk.h @@ -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 ); /** diff --git a/hawk/lib/run.c b/hawk/lib/run.c index d89ecf69..dc4f85b0 100644 --- a/hawk/lib/run.c +++ b/hawk/lib/run.c @@ -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; diff --git a/hawk/lib/val.c b/hawk/lib/val.c index bd190a6c..7acbcd62 100644 --- a/hawk/lib/val.c +++ b/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);