diff --git a/ghc/rts/GC.c b/ghc/rts/GC.c index 652131275c5fe4a2fab301010eee6b8f5e7e759a..17c056da882216e0d4627d8ab8737dba6734219b 100644 --- a/ghc/rts/GC.c +++ b/ghc/rts/GC.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------------- - * $Id: GC.c,v 1.21 1999/01/27 16:41:14 simonm Exp $ + * $Id: GC.c,v 1.22 1999/01/28 15:04:00 simonm Exp $ * * Two-space garbage collector * @@ -511,6 +511,9 @@ void GarbageCollect(void (*get_roots)(void)) } } + /* Guess the amount of live data for stats. */ + live = calcLive(); + /* Two-space collector: * Free the old to-space, and estimate the amount of live data. */ @@ -523,8 +526,6 @@ void GarbageCollect(void (*get_roots)(void)) for (bd = g0s0->to_space; bd != NULL; bd = bd->link) { bd->evacuated = 0; /* now from-space */ } - live = g0s0->to_blocks * BLOCK_SIZE_W + - ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_); /* For a two-space collector, we need to resize the nursery. */ @@ -563,71 +564,44 @@ void GarbageCollect(void (*get_roots)(void)) blocks = RtsFlags.GcFlags.minAllocAreaSize; } } + resizeNursery(blocks); - if (nursery_blocks < blocks) { - IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n", - blocks)); - g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks); - } else { - bdescr *next_bd; - - IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n", - blocks)); - for (bd = g0s0->blocks; nursery_blocks > blocks; nursery_blocks--) { - next_bd = bd->link; - freeGroup(bd); - bd = next_bd; - } - g0s0->blocks = bd; - } - - g0s0->n_blocks = nursery_blocks = blocks; - } else { /* Generational collector: - * estimate the amount of live data, and adjust the allocation - * area size if the user has given us a suggestion (+RTS -H<blah>) + * If the user has given us a suggested heap size, adjust our + * allocation area to make best use of the memory available. */ - live = 0; - for (g = 0; g < RtsFlags.GcFlags.generations; g++) { - for (s = 0; s < generations[g].n_steps; s++) { - /* approximate amount of live data (doesn't take into account slop - * at end of each block). ToDo: this more accurately. - */ - if (g == 0 && s == 0) { continue; } - step = &generations[g].steps[s]; - live += step->n_blocks * BLOCK_SIZE_W + - ((lnat)step->hp_bd->free -(lnat)step->hp_bd->start) / sizeof(W_); - } - } - if (RtsFlags.GcFlags.heapSizeSuggestion) { - nat avail_blocks = - (RtsFlags.GcFlags.heapSizeSuggestion - live / BLOCK_SIZE_W) / 2; - nat blocks; - - if (avail_blocks > RtsFlags.GcFlags.minAllocAreaSize) { - blocks = avail_blocks; + int blocks; + nat needed = calcNeeded(); /* approx blocks needed at next GC */ + + /* Guess how much will be live in generation 0 step 0 next time. + * A good approximation is the amount of data that was live this + * time: this assumes (1) that the size of G0S0 will be roughly + * the same as last time, and (2) that the promotion rate will be + * constant. + * + * If we don't know how much was live in G0S0 (because there's no + * step 1), then assume 30% (which is usually an overestimate). + */ + if (g0->n_steps == 1) { + needed += (g0s0->n_blocks * 30) / 100; } else { - blocks = RtsFlags.GcFlags.minAllocAreaSize; + needed += g0->steps[1].n_blocks; } - if (blocks > g0s0->n_blocks) { - /* need to add some blocks on */ - fprintf(stderr, "Increasing size of alloc area to %d blocks\n", blocks); - g0s0->blocks = allocNursery(g0s0->blocks, avail_blocks - g0s0->n_blocks); - } else { - bdescr *next_bd; - fprintf(stderr, "Decreasing size of alloc area to %d blocks\n", blocks); - for (bd = g0s0->blocks; nursery_blocks > blocks; nursery_blocks--) { - next_bd = bd->link; - freeGroup(bd); - bd = next_bd; - } - g0s0->blocks = bd; + /* Now we have a rough guess at the number of blocks needed for + * the next GC, subtract this from the user's suggested heap size + * and use the rest for the allocation area. + */ + blocks = (int)RtsFlags.GcFlags.heapSizeSuggestion - (int)needed; + + if (blocks < (int)RtsFlags.GcFlags.minAllocAreaSize) { + blocks = RtsFlags.GcFlags.minAllocAreaSize; } - g0s0->n_blocks = nursery_blocks = blocks; + + resizeNursery((nat)blocks); } } @@ -667,29 +641,9 @@ void GarbageCollect(void (*get_roots)(void)) scheduleFinalisers(old_weak_ptr_list); /* check sanity after GC */ -#ifdef DEBUG - if (RtsFlags.GcFlags.generations == 1) { - IF_DEBUG(sanity, checkHeap(g0s0->to_space, NULL)); - IF_DEBUG(sanity, checkChain(g0s0->large_objects)); - } else { - - for (g = 0; g <= N; g++) { - for (s = 0; s < generations[g].n_steps; s++) { - if (g == 0 && s == 0) { continue; } - IF_DEBUG(sanity, checkHeap(generations[g].steps[s].blocks, NULL)); - } - } - for (g = N+1; g < RtsFlags.GcFlags.generations; g++) { - for (s = 0; s < generations[g].n_steps; s++) { - IF_DEBUG(sanity, checkHeap(generations[g].steps[s].blocks, - generations[g].steps[s].blocks->start)); - IF_DEBUG(sanity, checkChain(generations[g].steps[s].large_objects)); - } - } - IF_DEBUG(sanity, checkFreeListSanity()); - } -#endif + IF_DEBUG(sanity, checkSanity(N)); + /* extra GC trace info */ IF_DEBUG(gc, stat_describe_gens()); #ifdef DEBUG diff --git a/ghc/rts/Storage.c b/ghc/rts/Storage.c index 5117375f58b4da6b52a3da45b0c8e68c712dcc8e..1f080c58a75493d08b3d2293428d67e033fbf918 100644 --- a/ghc/rts/Storage.c +++ b/ghc/rts/Storage.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------------- - * $Id: Storage.c,v 1.7 1999/01/26 16:16:30 simonm Exp $ + * $Id: Storage.c,v 1.8 1999/01/28 15:04:02 simonm Exp $ * * Storage manager front end * @@ -14,6 +14,7 @@ #include "MBlock.h" #include "gmp.h" #include "Weak.h" +#include "Sanity.h" #include "Storage.h" #include "StoragePriv.h" @@ -50,6 +51,11 @@ initStorage (void) step *step; generation *gen; + if (RtsFlags.GcFlags.heapSizeSuggestion > + RtsFlags.GcFlags.maxHeapSize) { + barf("Suggested heap size (-H<size>) is larger than max. heap size (-M<size>)\n"); + } + initBlockAllocator(); /* allocate generation info array */ @@ -171,6 +177,38 @@ allocNursery (bdescr *last_bd, nat blocks) return last_bd; } +extern void +resizeNursery ( nat blocks ) +{ + bdescr *bd; + + if (nursery_blocks == blocks) { + ASSERT(g0s0->n_blocks == blocks); + return; + } + + else if (nursery_blocks < blocks) { + IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n", + blocks)); + g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks); + } + + else { + bdescr *next_bd; + + IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n", + blocks)); + for (bd = g0s0->blocks; nursery_blocks > blocks; nursery_blocks--) { + next_bd = bd->link; + freeGroup(bd); + bd = next_bd; + } + g0s0->blocks = bd; + } + + g0s0->n_blocks = nursery_blocks = blocks; +} + void exitStorage (void) { @@ -344,6 +382,69 @@ stgDeallocForGMP (void *ptr STG_UNUSED, /* easy for us: the garbage collector does the dealloc'n */ } +/* ----------------------------------------------------------------------------- + Stats and stuff + -------------------------------------------------------------------------- */ + +/* Approximate the amount of live data in the heap. To be called just + * after garbage collection (see GarbageCollect()). + */ +extern lnat +calcLive(void) +{ + nat g, s; + lnat live = 0; + step *step; + + if (RtsFlags.GcFlags.generations == 1) { + live = g0s0->to_blocks * BLOCK_SIZE_W + + ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_); + } + + for (g = 0; g < RtsFlags.GcFlags.generations; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + /* approximate amount of live data (doesn't take into account slop + * at end of each block). + */ + if (g == 0 && s == 0) { + continue; + } + step = &generations[g].steps[s]; + live += step->n_blocks * BLOCK_SIZE_W + + ((lnat)step->hp_bd->free -(lnat)step->hp_bd->start) / sizeof(W_); + } + } + return live; +} + +/* Approximate the number of blocks that will be needed at the next + * garbage collection. + * + * Assume: all data currently live will remain live. Steps that will + * be collected next time will therefore need twice as many blocks + * since all the data will be copied. + */ +extern lnat +calcNeeded(void) +{ + lnat needed = 0; + nat g, s; + step *step; + + for (g = 0; g < RtsFlags.GcFlags.generations; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + if (g == 0 && s == 0) { continue; } + step = &generations[g].steps[s]; + if (generations[g].steps[0].n_blocks > generations[g].max_blocks) { + needed += 2 * step->n_blocks; + } else { + needed += step->n_blocks; + } + } + } + return needed; +} + /* ----------------------------------------------------------------------------- Debugging @@ -409,4 +510,33 @@ memInventory(void) #endif } +/* Full heap sanity check. */ + +extern void +checkSanity(nat N) +{ + nat g, s; + + if (RtsFlags.GcFlags.generations == 1) { + checkHeap(g0s0->to_space, NULL); + checkChain(g0s0->large_objects); + } else { + + for (g = 0; g <= N; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + if (g == 0 && s == 0) { continue; } + checkHeap(generations[g].steps[s].blocks, NULL); + } + } + for (g = N+1; g < RtsFlags.GcFlags.generations; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + checkHeap(generations[g].steps[s].blocks, + generations[g].steps[s].blocks->start); + checkChain(generations[g].steps[s].large_objects); + } + } + checkFreeListSanity(); + } +} + #endif diff --git a/ghc/rts/StoragePriv.h b/ghc/rts/StoragePriv.h index defe1425de16a66f99f50644c5c7a6e9a8a2f759..1c3266c04c0e8191048c5e4440626a10bf2f0b6c 100644 --- a/ghc/rts/StoragePriv.h +++ b/ghc/rts/StoragePriv.h @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------------- - * $Id: StoragePriv.h,v 1.5 1999/01/19 17:06:05 simonm Exp $ + * $Id: StoragePriv.h,v 1.6 1999/01/28 15:04:02 simonm Exp $ * * Internal Storage Manger Interface * @@ -106,7 +106,11 @@ extern nat nursery_blocks; extern nat alloc_blocks; extern nat alloc_blocks_lim; -extern bdescr *allocNursery (bdescr *last_bd, nat blocks); +extern bdescr *allocNursery ( bdescr *last_bd, nat blocks ); +extern void resizeNursery ( nat blocks ); + +extern lnat calcLive( void ); +extern lnat calcNeeded( void ); static inline void dbl_link_onto(bdescr *bd, bdescr **list) @@ -127,6 +131,7 @@ dbl_link_onto(bdescr *bd, bdescr **list) #ifdef DEBUG extern void memInventory(void); +extern void checkSanity(nat N); #endif #endif /* STORAGEPRIV_H */