From cacd714cd545bc3bd07882904055cfee162bd7de Mon Sep 17 00:00:00 2001 From: Simon Marlow Date: Wed, 31 Oct 2007 14:45:42 +0000 Subject: [PATCH] Remove the optimisation of avoiding scavenging for certain objects Some objects don't need to be scavenged, in particular if they have no pointers. This seems like an obvious optimisation, but in fact it only accounts for about 1% of objects (in GHC, for example), and the extra complication means it probably isn't worth doing. --- rts/Stats.c | 13 ++----- rts/Stats.h | 2 +- rts/sm/Evac.c | 101 +++--------------------------------------------------- rts/sm/GC.c | 34 +++--------------- rts/sm/GC.h | 6 +--- rts/sm/GCUtils.c | 38 ++------------------ 6 files changed, 16 insertions(+), 178 deletions(-) diff --git a/rts/Stats.c b/rts/Stats.c index f18e26f..1b0abaa 100644 --- a/rts/Stats.c +++ b/rts/Stats.c @@ -41,7 +41,6 @@ static Ticks ExitElapsedTime = 0; static ullong GC_tot_alloc = 0; static ullong GC_tot_copied = 0; -static ullong GC_tot_scavd_copied = 0; static Ticks GC_start_time = 0, GC_tot_time = 0; /* User GC Time */ static Ticks GCe_start_time = 0, GCe_tot_time = 0; /* Elapsed GC time */ @@ -288,8 +287,7 @@ stat_startGC(void) -------------------------------------------------------------------------- */ void -stat_endGC (lnat alloc, lnat live, lnat copied, - lnat scavd_copied, lnat gen) +stat_endGC (lnat alloc, lnat live, lnat copied, lnat gen) { if (RtsFlags.GcFlags.giveStats != NO_GC_STATS) { Ticks time, etime, gc_time, gc_etime; @@ -302,7 +300,7 @@ stat_endGC (lnat alloc, lnat live, lnat copied, nat faults = getPageFaults(); statsPrintf("%9ld %9ld %9ld", - alloc*sizeof(W_), (copied+scavd_copied)*sizeof(W_), + alloc*sizeof(W_), copied*sizeof(W_), live*sizeof(W_)); statsPrintf(" %5.2f %5.2f %7.2f %7.2f %4ld %4ld (Gen: %2ld)\n", TICK_TO_DBL(gc_time), @@ -320,7 +318,6 @@ stat_endGC (lnat alloc, lnat live, lnat copied, GC_coll_times[gen] += gc_time; GC_tot_copied += (ullong) copied; - GC_tot_scavd_copied += (ullong) scavd_copied; GC_tot_alloc += (ullong) alloc; GC_tot_time += gc_time; GCe_tot_time += gc_etime; @@ -522,12 +519,8 @@ stat_exit(int alloc) ullong_format_string(GC_tot_copied*sizeof(W_), temp, rtsTrue/*commas*/); - statsPrintf("%11s bytes copied during GC (scavenged)\n", temp); + statsPrintf("%11s bytes copied during GC\n", temp); - ullong_format_string(GC_tot_scavd_copied*sizeof(W_), - temp, rtsTrue/*commas*/); - statsPrintf("%11s bytes copied during GC (not scavenged)\n", temp); - if ( ResidencySamples > 0 ) { ullong_format_string(MaxResidency*sizeof(W_), temp, rtsTrue/*commas*/); diff --git a/rts/Stats.h b/rts/Stats.h index f7a14a1..2d7e655 100644 --- a/rts/Stats.h +++ b/rts/Stats.h @@ -16,7 +16,7 @@ void stat_endInit(void); void stat_startGC(void); void stat_endGC (lnat alloc, lnat live, - lnat copied, lnat scavd_copied, lnat gen); + lnat copied, lnat gen); #ifdef PROFILING void stat_startRP(void); diff --git a/rts/sm/Evac.c b/rts/sm/Evac.c index 14b80a7..891da0a 100644 --- a/rts/sm/Evac.c +++ b/rts/sm/Evac.c @@ -63,42 +63,6 @@ alloc_for_copy (nat size, step *stp) return to; } -STATIC_INLINE StgPtr -alloc_for_copy_noscav (nat size, step *stp) -{ - StgPtr to; - step_workspace *ws; - bdescr *bd; - - /* Find out where we're going, using the handy "to" pointer in - * the step of the source object. If it turns out we need to - * evacuate to an older generation, adjust it here (see comment - * by evacuate()). - */ - if (stp < gct->evac_step) { - if (gct->eager_promotion) { - stp = gct->evac_step; - } else { - gct->failed_to_evac = rtsTrue; - } - } - - ws = &gct->steps[stp->gen_no][stp->no]; - - /* chain a new block onto the to-space for the destination step if - * necessary. - */ - bd = ws->scavd_list; - to = bd->free; - if (to + size >= bd->start + BLOCK_SIZE_W) { - bd = gc_alloc_scavd_block(ws); - to = bd->free; - } - bd->free = to + size; - - return to; -} - STATIC_INLINE void copy_tag(StgClosure **p, StgClosure *src, nat size, step *stp,StgWord tag) { @@ -152,57 +116,6 @@ copy_tag(StgClosure **p, StgClosure *src, nat size, step *stp,StgWord tag) #endif } - -// Same as copy() above, except the object will be allocated in memory -// that will not be scavenged. Used for object that have no pointer -// fields. -STATIC_INLINE void -copy_noscav_tag(StgClosure **p, StgClosure *src, nat size, step *stp, StgWord tag) -{ - StgPtr to, tagged_to, from; - nat i; - StgWord info; - -#ifdef THREADED_RTS - do { - info = xchg((StgPtr)&src->header.info, (W_)&stg_WHITEHOLE_info); - } while (info == (W_)&stg_WHITEHOLE_info); - if (info == (W_)&stg_EVACUATED_info) { - src->header.info = (const StgInfoTable *)info; - return evacuate(p); // does the failed_to_evac stuff - } -#else - info = (W_)src->header.info; - src->header.info = &stg_EVACUATED_info; -#endif - - to = alloc_for_copy_noscav(size,stp); - tagged_to = (StgPtr)TAG_CLOSURE(tag,(StgClosure*)to); - *p = (StgClosure *)tagged_to; - - TICK_GC_WORDS_COPIED(size); - - from = (StgPtr)src; - to[0] = info; - for (i = 1; i < size; i++) { // unroll for small i - to[i] = from[i]; - } - - ((StgEvacuated*)from)->evacuee = (StgClosure *)tagged_to; - -#ifdef THREADED_RTS - write_barrier(); - ((StgEvacuated*)from)->header.info = &stg_EVACUATED_info; -#endif - -#ifdef PROFILING - // We store the size of the just evacuated object in the LDV word so that - // the profiler can guess the position of the next object later. - SET_EVACUAEE_FOR_LDV(from, size); -#endif -} - - /* Special version of copy() for when we only want to copy the info * pointer of an object, but reserve some padding after it. This is * used to optimise evacuation of BLACKHOLEs. @@ -226,7 +139,7 @@ copyPart(StgClosure **p, StgClosure *src, nat size_to_reserve, nat size_to_copy, info = (W_)src->header.info; src->header.info = &stg_EVACUATED_info; #endif - + to = alloc_for_copy(size_to_reserve, stp); *p = (StgClosure *)to; @@ -262,12 +175,6 @@ copy(StgClosure **p, StgClosure *src, nat size, step *stp) copy_tag(p,src,size,stp,0); } -STATIC_INLINE void -copy_noscav(StgClosure **p, StgClosure *src, nat size, step *stp) -{ - copy_noscav_tag(p,src,size,stp,0); -} - /* ----------------------------------------------------------------------------- Evacuate a large object @@ -561,7 +468,7 @@ loop: ); } else { - copy_noscav_tag(p,q,sizeofW(StgHeader)+1,stp,tag); + copy_tag(p,q,sizeofW(StgHeader)+1,stp,tag); } return; } @@ -599,7 +506,7 @@ loop: return; case CONSTR_0_2: - copy_noscav_tag(p,q,sizeofW(StgHeader)+2,stp,tag); + copy_tag(p,q,sizeofW(StgHeader)+2,stp,tag); return; case THUNK: @@ -693,7 +600,7 @@ loop: case ARR_WORDS: // just copy the block - copy_noscav(p,q,arr_words_sizeW((StgArrWords *)q),stp); + copy(p,q,arr_words_sizeW((StgArrWords *)q),stp); return; case MUT_ARR_PTRS_CLEAN: diff --git a/rts/sm/GC.c b/rts/sm/GC.c index 4ca0c9d..2fc3f4d 100644 --- a/rts/sm/GC.c +++ b/rts/sm/GC.c @@ -122,7 +122,6 @@ gc_thread *gc_threads = NULL; // For stats: long copied; // *words* copied & scavenged during this GC -long scavd_copied; // *words* copied only during this GC #ifdef THREADED_RTS SpinLock recordMutableGen_sync; @@ -310,7 +309,6 @@ GarbageCollect ( rtsBool force_major_gc ) // Initialise stats copied = 0; - scavd_copied = 0; // this is the main thread gct = &gc_threads[0]; @@ -434,10 +432,6 @@ GarbageCollect ( rtsBool force_major_gc ) // Push the final block if (ws->scan_bd) { push_scan_block(ws->scan_bd, ws); } - // update stats: we haven't counted the block at the - // front of the scavd_list yet. - scavd_copied += ws->scavd_list->free - ws->scavd_list->start; - ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks); prev = ws->scavd_list; @@ -672,7 +666,7 @@ GarbageCollect ( rtsBool force_major_gc ) #endif // ok, GC over: tell the stats department what happened. - stat_endGC(allocated, live, copied, scavd_copied, N); + stat_endGC(allocated, live, copied, N); #if defined(RTS_USER_SIGNALS) if (RtsFlags.MiscFlags.install_signal_handlers) { @@ -1182,12 +1176,8 @@ init_collected_gen (nat g, nat n_threads) ws->buffer_todo_bd = NULL; gc_alloc_todo_block(ws); - // allocate a block for "already scavenged" objects. This goes - // on the front of the stp->blocks list, so it won't be - // traversed by the scavenging sweep. ws->scavd_list = NULL; ws->n_scavd_blocks = 0; - gc_alloc_scavd_block(ws); } } } @@ -1220,6 +1210,9 @@ init_uncollected_gen (nat g, nat threads) ws->buffer_todo_bd = NULL; ws->todo_large_objects = NULL; + ws->scavd_list = NULL; + ws->n_scavd_blocks = 0; + // If the block at the head of the list in this generation // is less than 3/4 full, then use it as a todo block. if (isPartiallyFull(stp->blocks)) @@ -1245,25 +1238,6 @@ init_uncollected_gen (nat g, nat threads) ws->todo_bd = NULL; gc_alloc_todo_block(ws); } - - // Do the same trick for the scavd block - if (isPartiallyFull(stp->blocks)) - { - ws->scavd_list = stp->blocks; - stp->blocks = stp->blocks->link; - stp->n_blocks -= 1; - ws->scavd_list->link = NULL; - ws->n_scavd_blocks = 1; - // subtract the contents of this block from the stats, - // because we'll count the whole block later. - scavd_copied -= ws->scavd_list->free - ws->scavd_list->start; - } - else - { - ws->scavd_list = NULL; - ws->n_scavd_blocks = 0; - gc_alloc_scavd_block(ws); - } } } diff --git a/rts/sm/GC.h b/rts/sm/GC.h index d45efb9..488a2db 100644 --- a/rts/sm/GC.h +++ b/rts/sm/GC.h @@ -88,11 +88,7 @@ typedef struct step_workspace_ { // where large objects to be scavenged go bdescr * todo_large_objects; - // Objects that need not be, or have already been, scavenged. The - // block at the front of the list is special: objects that don't - // need to be scavenged are copied directly to this block. - // Completed scan blocks also go on this list; but we put them - // after the head block. + // Objects that need not be, or have already been, scavenged. bdescr * scavd_list; lnat n_scavd_blocks; // count of blocks in this list diff --git a/rts/sm/GCUtils.c b/rts/sm/GCUtils.c index cee17c4..a65131a 100644 --- a/rts/sm/GCUtils.c +++ b/rts/sm/GCUtils.c @@ -82,11 +82,9 @@ push_scan_block (bdescr *bd, step_workspace *ws) // update stats: this is a block that has been copied & scavenged copied += bd->free - bd->start; - // put the scan block *second* in ws->scavd_list. The first block - // in this list is for evacuating objects that don't need to be - // scavenged. - bd->link = ws->scavd_list->link; - ws->scavd_list->link = bd; + // put the scan block on the ws->scavd_list. + bd->link = ws->scavd_list; + ws->scavd_list = bd; ws->n_scavd_blocks ++; IF_DEBUG(sanity, @@ -130,36 +128,6 @@ gc_alloc_todo_block (step_workspace *ws) return bd; } -bdescr * -gc_alloc_scavd_block (step_workspace *ws) -{ - bdescr *bd; - - bd = allocBlock_sync(); - - bd->gen_no = ws->stp->gen_no; - bd->step = ws->stp; - - // blocks in to-space in generations up to and including N - // get the BF_EVACUATED flag. - if (ws->stp->gen_no <= N) { - bd->flags = BF_EVACUATED; - } else { - bd->flags = 0; - } - - // update stats: this is a block that has been copied only - if (ws->scavd_list != NULL) { - scavd_copied += ws->scavd_list->free - ws->scavd_list->start; - } - - bd->link = ws->scavd_list; - ws->scavd_list = bd; - ws->n_scavd_blocks++; - - return bd; -} - /* ----------------------------------------------------------------------------- * Debugging * -------------------------------------------------------------------------- */ -- 1.7.10.4