Wed Jul 7 10:39:00 BRT 2010 Marco TĂșlio Gontijo e Silva * Immix: allocate and free memory in lines. diff -rN -u old-ghc-temp/includes/rts/storage/Block.h new-ghc-temp-1/includes/rts/storage/Block.h --- old-ghc-temp/includes/rts/storage/Block.h 2010-07-07 19:25:40.000000000 -0300 +++ new-ghc-temp-1/includes/rts/storage/Block.h 2010-07-07 19:25:41.000000000 -0300 @@ -98,6 +98,8 @@ #define BF_FRAGMENTED 64 /* we know about this block (for finding leaks) */ #define BF_KNOWN 128 +/* Block contains objects larger than a line */ +#define BF_MEDIUM 256 /* Finding the block descriptor for a given block -------------------------- */ diff -rN -u old-ghc-temp/includes/rts/storage/GC.h new-ghc-temp-1/includes/rts/storage/GC.h --- old-ghc-temp/includes/rts/storage/GC.h 2010-07-07 19:25:40.000000000 -0300 +++ new-ghc-temp-1/includes/rts/storage/GC.h 2010-07-07 19:25:41.000000000 -0300 @@ -12,6 +12,13 @@ #include #include "rts/OSThreads.h" +// This data type represents a free line group. Each generation has a linked +// list of these groups with various size. +typedef struct line_ { + struct line_ *next; // A link to the next free line group + StgWord size; // The size of this group +} line; + /* ----------------------------------------------------------------------------- * Generational GC * @@ -112,6 +119,13 @@ bdescr * bitmap; // bitmap for compacting collection StgTSO * old_threads; + + // The first free line group of this generation + line * first_line; + + // Line allocation + StgPtr line_free; + StgPtr line_lim; } generation; extern generation * generations; diff -rN -u old-ghc-temp/rts/sm/Evac.c new-ghc-temp-1/rts/sm/Evac.c --- old-ghc-temp/rts/sm/Evac.c 2010-07-07 19:25:40.000000000 -0300 +++ new-ghc-temp-1/rts/sm/Evac.c 2010-07-07 19:25:41.000000000 -0300 @@ -69,9 +69,46 @@ } } + // Currently the mark stack is used to ensure that the allocated object + // gets scavenged, so allocation in lines only happens when the mark stack + // is active, that is, in major GCs. + if (major_gc) { + + // No free space left on the current line group, get next one. + if (gen->line_free + size > gen->line_lim) { + + // Check if there is another free line group, and if the object + // fits in it. + if (gen->first_line != NULL && + size <= BITS_IN(W_) * gen->first_line->size) { + + gen->line_free = (StgPtr) gen->first_line; + gen->line_lim = gen->line_free + BITS_IN(W_) * + gen->first_line->size; + gen->first_line = gen->first_line->next; + + } + + // If there's no space in the current line group, and there isn't + // another free line group, or the object doesn't fit in the next + // one, allocate in blocks. + else { + goto block; + } + } + to = gen->line_free; + gen->line_free += size; + + mark(to, Bdescr(to)); + push_mark_stack(to); + + return to; + } + +block: ws = &gct->gens[gen->no]; // this compiles to a single mem access to gen->abs_no only - + /* chain a new block onto the to-space for the destination gen if * necessary. */ @@ -98,6 +135,13 @@ to = alloc_for_copy(size,gen); + // Mark this block as containing medium objects, that is, larger than the + // size of a line. This will make Immix unavailable for this block, in + // Sweep.c. + if(size > BITS_IN(W_)) { + Bdescr(to)->flags |= BF_MEDIUM; + } + from = (StgPtr)src; to[0] = (W_)info; diff -rN -u old-ghc-temp/rts/sm/Storage.c new-ghc-temp-1/rts/sm/Storage.c --- old-ghc-temp/rts/sm/Storage.c 2010-07-07 19:25:40.000000000 -0300 +++ new-ghc-temp-1/rts/sm/Storage.c 2010-07-07 19:25:41.000000000 -0300 @@ -89,6 +89,7 @@ #endif gen->threads = END_TSO_QUEUE; gen->old_threads = END_TSO_QUEUE; + gen->first_line = NULL; } void diff -rN -u old-ghc-temp/rts/sm/Sweep.c new-ghc-temp-1/rts/sm/Sweep.c --- old-ghc-temp/rts/sm/Sweep.c 2010-07-07 19:25:40.000000000 -0300 +++ new-ghc-temp-1/rts/sm/Sweep.c 2010-07-07 19:25:41.000000000 -0300 @@ -36,7 +36,13 @@ // freed, fragd and blocks are only used for debugging // resid is the number of BITS_IN(W_) words groups that contains a mark nat freed, resid, fragd, blocks, live; + line *last_line; + gen->first_line = NULL; + gen->line_free = NULL; + gen->line_lim = NULL; + last_line = NULL; + ASSERT(countBlocks(gen->old_blocks) == gen->n_old_blocks); live = 0; // estimate of live data in this gen @@ -64,9 +70,6 @@ { if (bd->u.bitmap[i] != 0) resid++; } - // This estimate assumes that all 8 words groups that have at least one - // mark are completly full, so it's the number of live data is always - // less or equal than gen->live_estimate. live += resid * BITS_IN(W_); // All the block is unmarked: free it @@ -85,11 +88,67 @@ else { prev = bd; - // if more than 1/4 of the word groups are completely umnarked, - // the block is fragmented. - if (resid < (BLOCK_SIZE_W * 3) / (BITS_IN(W_) * 4)) { - fragd++; - bd->flags |= BF_FRAGMENTED; + if (bd->flags & BF_MEDIUM) { + // if more than 1/4 of the word groups are completely umnarked, + // the block is fragmented. + if(resid < (BLOCK_SIZE_W * 3) / (BITS_IN(W_) * 4)) { + fragd++; + bd->flags |= BF_FRAGMENTED; + } + } + else { + rtsBool sequence; + sequence = rtsFalse; + + for (i = 1; i < BLOCK_SIZE_W / BITS_IN(W_); i++) { + StgPtr start; + start = bd->start + BITS_IN(W_) * i; + + // Tests if this is the first line we've found in a free line + // group. Due to conservative marking, we don't consider the + // first free line of a line group as free. + if (bd->u.bitmap[i] == 0 && bd->u.bitmap[i - 1] == 0 && + + // Don't create free lines in the unalocated area of the + // block. + start + BITS_IN(W_) <= bd->free) { + + // This is the first line of the generation. + if (gen->first_line == NULL) { + gen->first_line = (line *) start; + } + + // This is a line inside a free line group, so all that + // is needed is to increment the size of the group. + if (sequence) { + last_line->size++; + } + + // This is the first line of a group. + else { + + // If this is not the first group of the + // generation, update the link of the last group. + if(last_line != NULL) { + last_line->next = (line *) start; + } + last_line = (line *) start; + + // Finishes the list + last_line->next = NULL; + + // Sets the size + last_line->size = 1; + } + sequence = rtsTrue; + } + + // This is not a free line. If a new free line is found, + // it'll be the first of a free line group. + else { + sequence = rtsFalse; + } + } } } }