X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=rts%2Fsm%2FGCUtils.c;h=ef8d0bd56d786a73e61b6eae2b6ad311379a2418;hp=103430abedf084cd308dd40d5d2b8bd2608aac6f;hb=1fb38442d3a55ac92795aa6c5ed4df82011df724;hpb=eea6454f0bcbec6c6612e963cd85702c475ef146 diff --git a/rts/sm/GCUtils.c b/rts/sm/GCUtils.c index 103430a..ef8d0bd 100644 --- a/rts/sm/GCUtils.c +++ b/rts/sm/GCUtils.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------------- * - * (c) The GHC Team 1998-2006 + * (c) The GHC Team 1998-2008 * * Generational garbage collector: utilities * @@ -11,13 +11,20 @@ * * ---------------------------------------------------------------------------*/ +#include "PosixSource.h" #include "Rts.h" -#include "RtsFlags.h" + +#include "BlockAlloc.h" #include "Storage.h" #include "GC.h" +#include "GCThread.h" +#include "GCTDecl.h" #include "GCUtils.h" #include "Printer.h" #include "Trace.h" +#ifdef THREADED_RTS +#include "WSDeque.h" +#endif #ifdef THREADED_RTS SpinLock gc_alloc_block_sync; @@ -33,6 +40,41 @@ allocBlock_sync(void) return bd; } +static bdescr * +allocGroup_sync(nat n) +{ + bdescr *bd; + ACQUIRE_SPIN_LOCK(&gc_alloc_block_sync); + bd = allocGroup(n); + RELEASE_SPIN_LOCK(&gc_alloc_block_sync); + return bd; +} + + +#if 0 +static void +allocBlocks_sync(nat n, bdescr **hd, bdescr **tl, + nat gen_no, step *stp, + StgWord32 flags) +{ + bdescr *bd; + nat i; + ACQUIRE_SPIN_LOCK(&gc_alloc_block_sync); + bd = allocGroup(n); + for (i = 0; i < n; i++) { + bd[i].blocks = 1; + bd[i].gen_no = gen_no; + bd[i].step = stp; + bd[i].flags = flags; + bd[i].link = &bd[i+1]; + bd[i].u.scan = bd[i].free = bd[i].start; + } + *hd = bd; + *tl = &bd[n-1]; + RELEASE_SPIN_LOCK(&gc_alloc_block_sync); +} +#endif + void freeChain_sync(bdescr *bd) { @@ -46,112 +88,203 @@ freeChain_sync(bdescr *bd) -------------------------------------------------------------------------- */ bdescr * -grab_todo_block (step_workspace *ws) +grab_local_todo_block (gen_workspace *ws) { bdescr *bd; - step *stp; + generation *gen; - stp = ws->stp; - bd = NULL; + gen = ws->gen; - if (ws->buffer_todo_bd) + bd = ws->todo_overflow; + if (bd != NULL) + { + ws->todo_overflow = bd->link; + bd->link = NULL; + ws->n_todo_overflow--; + return bd; + } + + bd = popWSDeque(ws->todo_q); + if (bd != NULL) { - bd = ws->buffer_todo_bd; ASSERT(bd->link == NULL); - ws->buffer_todo_bd = NULL; return bd; } - ACQUIRE_SPIN_LOCK(&stp->sync_todo); - if (stp->todos) { - bd = stp->todos; - stp->todos = bd->link; - stp->n_todos--; - bd->link = NULL; - } - RELEASE_SPIN_LOCK(&stp->sync_todo); - return bd; + return NULL; } -static void -push_todo_block (bdescr *bd, step *stp) +#if defined(THREADED_RTS) +bdescr * +steal_todo_block (nat g) { - ASSERT(bd->link == NULL); - ACQUIRE_SPIN_LOCK(&stp->sync_todo); - bd->link = stp->todos; - stp->todos = bd; - stp->n_todos++; - trace(TRACE_gc, "step %d, n_todos: %d", stp->abs_no, stp->n_todos); - RELEASE_SPIN_LOCK(&stp->sync_todo); + nat n; + bdescr *bd; + + // look for work to steal + for (n = 0; n < n_gc_threads; n++) { + if (n == gct->thread_index) continue; + bd = stealWSDeque(gc_threads[n]->gens[g].todo_q); + if (bd) { + return bd; + } + } + return NULL; } +#endif void -push_scan_block (bdescr *bd, step_workspace *ws) +push_scanned_block (bdescr *bd, gen_workspace *ws) { ASSERT(bd != NULL); ASSERT(bd->link == NULL); + ASSERT(bd->gen == ws->gen); + ASSERT(bd->u.scan == bd->free); - // update stats: this is a block that has been copied & scavenged - copied += bd->free - bd->start; - - // 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, - ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks)); + if (bd->start + bd->blocks * BLOCK_SIZE_W - bd->free > WORK_UNIT_WORDS) + { + // a partially full block: put it on the part_list list. + bd->link = ws->part_list; + ws->part_list = bd; + ws->n_part_blocks += bd->blocks; + IF_DEBUG(sanity, + ASSERT(countBlocks(ws->part_list) == ws->n_part_blocks)); + } + else + { + // put the scan block on the ws->scavd_list. + bd->link = ws->scavd_list; + ws->scavd_list = bd; + ws->n_scavd_blocks += bd->blocks; + IF_DEBUG(sanity, + ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks)); + } } StgPtr -gc_alloc_todo_block (step_workspace *ws) +todo_block_full (nat size, gen_workspace *ws) { + StgPtr p; bdescr *bd; - if (ws->todo_bd != NULL) { - ws->todo_bd->free = ws->todo_free; + // todo_free has been pre-incremented by Evac.c:alloc_for_copy(). We + // are expected to leave it bumped when we've finished here. + ws->todo_free -= size; + + bd = ws->todo_bd; + + ASSERT(bd != NULL); + ASSERT(bd->link == NULL); + ASSERT(bd->gen == ws->gen); + + // If the global list is not empty, or there's not much work in + // this block to push, and there's enough room in + // this block to evacuate the current object, then just increase + // the limit. + if (!looksEmptyWSDeque(ws->todo_q) || + (ws->todo_free - bd->u.scan < WORK_UNIT_WORDS / 2)) { + if (ws->todo_free + size < bd->start + bd->blocks * BLOCK_SIZE_W) { + ws->todo_lim = stg_min(bd->start + bd->blocks * BLOCK_SIZE_W, + ws->todo_lim + stg_max(WORK_UNIT_WORDS,size)); + debugTrace(DEBUG_gc, "increasing limit for %p to %p", bd->start, ws->todo_lim); + p = ws->todo_free; + ws->todo_free += size; + return p; + } } + + gct->copied += ws->todo_free - bd->free; + bd->free = ws->todo_free; + + ASSERT(bd->u.scan >= bd->start && bd->u.scan <= bd->free); - // If we already have a todo block, it must be full, so we push it - // out: first to the buffer_todo_bd, then to the step. BUT, don't - // push out the block out if it is already the scan block. - if (ws->todo_bd != NULL && ws->scan_bd != ws->todo_bd) { - ASSERT(ws->todo_bd->link == NULL); - if (ws->buffer_todo_bd == NULL) { - // If the global todo list is empty, push this block - // out immediately rather than caching it in - // buffer_todo_bd, because there might be other threads - // waiting for work. - if (ws->stp->todos == NULL) { - push_todo_block(ws->todo_bd, ws->stp); - } else { - ws->buffer_todo_bd = ws->todo_bd; + // If this block is not the scan block, we want to push it out and + // make room for a new todo block. + if (bd != gct->scan_bd) + { + // If this block does not have enough space to allocate the + // current object, but it also doesn't have any work to push, then + // push it on to the scanned list. It cannot be empty, because + // then there would be enough room to copy the current object. + if (bd->u.scan == bd->free) + { + ASSERT(bd->free != bd->start); + push_scanned_block(bd, ws); + } + // Otherwise, push this block out to the global list. + else + { + generation *gen; + gen = ws->gen; + debugTrace(DEBUG_gc, "push todo block %p (%ld words), step %d, todo_q: %ld", + bd->start, (unsigned long)(bd->free - bd->u.scan), + gen->no, dequeElements(ws->todo_q)); + + if (!pushWSDeque(ws->todo_q, bd)) { + bd->link = ws->todo_overflow; + ws->todo_overflow = bd; + ws->n_todo_overflow++; } - } else { - ASSERT(ws->buffer_todo_bd->link == NULL); - push_todo_block(ws->buffer_todo_bd, ws->stp); - ws->buffer_todo_bd = ws->todo_bd; } - ws->todo_bd = NULL; - } + } - bd = allocBlock_sync(); + ws->todo_bd = NULL; + ws->todo_free = NULL; + ws->todo_lim = NULL; - bd->gen_no = ws->stp->gen_no; - bd->step = ws->stp; - bd->link = NULL; + alloc_todo_block(ws, size); + + p = ws->todo_free; + ws->todo_free += size; + return p; +} - // 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; +StgPtr +alloc_todo_block (gen_workspace *ws, nat size) +{ + bdescr *bd/*, *hd, *tl */; + + // Grab a part block if we have one, and it has enough room + bd = ws->part_list; + if (bd != NULL && + bd->start + bd->blocks * BLOCK_SIZE_W - bd->free > (int)size) + { + ws->part_list = bd->link; + ws->n_part_blocks -= bd->blocks; } - + else + { + // blocks in to-space get the BF_EVACUATED flag. + +// allocBlocks_sync(16, &hd, &tl, +// ws->step->gen_no, ws->step, BF_EVACUATED); +// +// tl->link = ws->part_list; +// ws->part_list = hd->link; +// ws->n_part_blocks += 15; +// +// bd = hd; + + if (size > BLOCK_SIZE_W) { + bd = allocGroup_sync((lnat)BLOCK_ROUND_UP(size*sizeof(W_)) + / BLOCK_SIZE); + } else { + bd = allocBlock_sync(); + } + initBdescr(bd, ws->gen, ws->gen->to); + bd->flags = BF_EVACUATED; + bd->u.scan = bd->free = bd->start; + } + + bd->link = NULL; + ws->todo_bd = bd; - ws->todo_free = bd->start; - ws->todo_lim = bd->start + BLOCK_SIZE_W; + ws->todo_free = bd->free; + ws->todo_lim = stg_min(bd->start + bd->blocks * BLOCK_SIZE_W, + bd->free + stg_max(WORK_UNIT_WORDS,size)); + + debugTrace(DEBUG_gc, "alloc new todo block %p for gen %d", + bd->free, ws->gen->no); return ws->todo_free; } @@ -162,14 +295,13 @@ gc_alloc_todo_block (step_workspace *ws) #if DEBUG void -printMutableList(generation *gen) +printMutableList(bdescr *bd) { - bdescr *bd; StgPtr p; - debugBelch("mutable list %p: ", gen->mut_list); + debugBelch("mutable list %p: ", bd); - for (bd = gen->mut_list; bd != NULL; bd = bd->link) { + for (; bd != NULL; bd = bd->link) { for (p = bd->start; p < bd->free; p++) { debugBelch("%p (%s), ", (void *)*p, info_type((StgClosure *)*p)); }