Code GC: segregated free list for faster allocation, combine unmark/build free list/update literals passes into one pass for faster deallocation

db4
Slava Pestov 2009-04-26 09:15:58 -05:00
parent c2490f4038
commit 5f756a8019
3 changed files with 119 additions and 70 deletions

View File

@ -1,5 +1,10 @@
#include "master.h" #include "master.h"
static void clear_free_list(F_HEAP *heap)
{
memset(&heap->free,0,sizeof(F_HEAP_FREE_LIST));
}
/* This malloc-style heap code is reasonably generic. Maybe in the future, it /* This malloc-style heap code is reasonably generic. Maybe in the future, it
will be used for the data heap too, if we ever get incremental will be used for the data heap too, if we ever get incremental
mark/sweep/compact GC. */ mark/sweep/compact GC. */
@ -8,17 +13,23 @@ void new_heap(F_HEAP *heap, CELL size)
heap->segment = alloc_segment(align_page(size)); heap->segment = alloc_segment(align_page(size));
if(!heap->segment) if(!heap->segment)
fatal_error("Out of memory in new_heap",size); fatal_error("Out of memory in new_heap",size);
heap->free_list = NULL;
clear_free_list(heap);
} }
/* If there is no previous block, next_free becomes the head of the free list, void add_to_free_list(F_HEAP *heap, F_FREE_BLOCK *block)
else its linked in */
INLINE void update_free_list(F_HEAP *heap, F_FREE_BLOCK *prev, F_FREE_BLOCK *next_free)
{ {
if(prev) if(block->block.size < FREE_LIST_COUNT * BLOCK_SIZE_INCREMENT)
prev->next_free = next_free; {
int index = block->block.size / BLOCK_SIZE_INCREMENT;
block->next_free = heap->free.small[index];
heap->free.small[index] = block;
}
else else
heap->free_list = next_free; {
block->next_free = heap->free.large;
heap->free.large = block;
}
} }
/* Called after reading the code heap from the image file, and after code GC. /* Called after reading the code heap from the image file, and after code GC.
@ -28,7 +39,11 @@ compiling.limit. */
void build_free_list(F_HEAP *heap, CELL size) void build_free_list(F_HEAP *heap, CELL size)
{ {
F_BLOCK *prev = NULL; F_BLOCK *prev = NULL;
F_FREE_BLOCK *prev_free = NULL;
clear_free_list(heap);
size = (size + BLOCK_SIZE_INCREMENT - 1) & ~(BLOCK_SIZE_INCREMENT - 1);
F_BLOCK *scan = first_block(heap); F_BLOCK *scan = first_block(heap);
F_FREE_BLOCK *end = (F_FREE_BLOCK *)(heap->segment->start + size); F_FREE_BLOCK *end = (F_FREE_BLOCK *)(heap->segment->start + size);
@ -38,8 +53,7 @@ void build_free_list(F_HEAP *heap, CELL size)
switch(scan->status) switch(scan->status)
{ {
case B_FREE: case B_FREE:
update_free_list(heap,prev_free,(F_FREE_BLOCK *)scan); add_to_free_list(heap,(F_FREE_BLOCK *)scan);
prev_free = (F_FREE_BLOCK *)scan;
break; break;
case B_ALLOCATED: case B_ALLOCATED:
break; break;
@ -58,10 +72,9 @@ void build_free_list(F_HEAP *heap, CELL size)
{ {
end->block.status = B_FREE; end->block.status = B_FREE;
end->block.size = heap->segment->end - (CELL)end; end->block.size = heap->segment->end - (CELL)end;
end->next_free = NULL;
/* add final free block */ /* add final free block */
update_free_list(heap,prev_free,end); add_to_free_list(heap,end);
} }
/* This branch is taken if the newly loaded image fits exactly, or /* This branch is taken if the newly loaded image fits exactly, or
after code GC */ after code GC */
@ -70,62 +83,87 @@ void build_free_list(F_HEAP *heap, CELL size)
/* even if there's no room at the end of the heap for a new /* even if there's no room at the end of the heap for a new
free block, we might have to jigger it up by a few bytes in free block, we might have to jigger it up by a few bytes in
case prev + prev->size */ case prev + prev->size */
if(prev) if(prev) prev->size = heap->segment->end - (CELL)prev;
prev->size = heap->segment->end - (CELL)prev;
/* this is the last free block */
update_free_list(heap,prev_free,NULL);
} }
} }
static void assert_free_block(F_FREE_BLOCK *block)
{
if(block->block.status != B_FREE)
critical_error("Invalid block in free list",(CELL)block);
}
F_FREE_BLOCK *find_free_block(F_HEAP *heap, CELL size)
{
CELL attempt = size;
while(attempt < FREE_LIST_COUNT * BLOCK_SIZE_INCREMENT)
{
int index = attempt / BLOCK_SIZE_INCREMENT;
F_FREE_BLOCK *block = heap->free.small[index];
if(block)
{
assert_free_block(block);
heap->free.small[index] = block->next_free;
return block;
}
attempt *= 2;
}
F_FREE_BLOCK *prev = NULL;
F_FREE_BLOCK *block = heap->free.large;
while(block)
{
assert_free_block(block);
if(block->block.size >= size)
{
if(prev)
prev->next_free = block->next_free;
else
heap->free.large = block->next_free;
return block;
}
prev = block;
block = block->next_free;
}
return NULL;
}
F_FREE_BLOCK *split_free_block(F_HEAP *heap, F_FREE_BLOCK *block, CELL size)
{
if(block->block.size != size )
{
/* split the block in two */
F_FREE_BLOCK *split = (F_FREE_BLOCK *)((CELL)block + size);
split->block.status = B_FREE;
split->block.size = block->block.size - size;
split->next_free = block->next_free;
block->block.size = size;
add_to_free_list(heap,split);
}
return block;
}
/* Allocate a block of memory from the mark and sweep GC heap */ /* Allocate a block of memory from the mark and sweep GC heap */
F_BLOCK *heap_allot(F_HEAP *heap, CELL size) F_BLOCK *heap_allot(F_HEAP *heap, CELL size)
{ {
F_FREE_BLOCK *prev = NULL; size = (size + BLOCK_SIZE_INCREMENT - 1) & ~(BLOCK_SIZE_INCREMENT - 1);
F_FREE_BLOCK *scan = heap->free_list;
size = (size + 31) & ~31; F_FREE_BLOCK *block = find_free_block(heap,size);
if(block)
while(scan)
{ {
if(scan->block.status != B_FREE) block = split_free_block(heap,block,size);
critical_error("Invalid block in free list",(CELL)scan);
if(scan->block.size < size) block->block.status = B_ALLOCATED;
{ return &block->block;
prev = scan;
scan = scan->next_free;
continue;
}
/* we found a candidate block */
F_FREE_BLOCK *next_free;
if(scan->block.size - size <= sizeof(F_BLOCK) * 2)
{
/* too small to be split */
next_free = scan->next_free;
} }
else else
{
/* split the block in two */
F_FREE_BLOCK *split = (F_FREE_BLOCK *)((CELL)scan + size);
split->block.status = B_FREE;
split->block.size = scan->block.size - size;
split->next_free = scan->next_free;
scan->block.size = size;
next_free = split;
}
/* update the free list */
update_free_list(heap,prev,next_free);
/* this is our new block */
scan->block.status = B_ALLOCATED;
return &scan->block;
}
return NULL; return NULL;
} }
@ -162,8 +200,10 @@ void unmark_marked(F_HEAP *heap)
/* After code GC, all referenced code blocks have status set to B_MARKED, so any /* After code GC, all referenced code blocks have status set to B_MARKED, so any
which are allocated and not marked can be reclaimed. */ which are allocated and not marked can be reclaimed. */
void free_unmarked(F_HEAP *heap) void free_unmarked(F_HEAP *heap, HEAP_ITERATOR iter)
{ {
clear_free_list(heap);
F_BLOCK *prev = NULL; F_BLOCK *prev = NULL;
F_BLOCK *scan = first_block(heap); F_BLOCK *scan = first_block(heap);
@ -183,10 +223,15 @@ void free_unmarked(F_HEAP *heap)
case B_FREE: case B_FREE:
if(prev && prev->status == B_FREE) if(prev && prev->status == B_FREE)
prev->size += scan->size; prev->size += scan->size;
else
prev = scan;
break; break;
case B_MARKED: case B_MARKED:
if(prev && prev->status == B_FREE)
add_to_free_list(heap,(F_FREE_BLOCK *)prev);
scan->status = B_ALLOCATED; scan->status = B_ALLOCATED;
prev = scan; prev = scan;
iter(scan);
break; break;
default: default:
critical_error("Invalid scan->status",(CELL)scan); critical_error("Invalid scan->status",(CELL)scan);
@ -195,7 +240,8 @@ void free_unmarked(F_HEAP *heap)
scan = next_block(heap,scan); scan = next_block(heap,scan);
} }
build_free_list(heap,heap->segment->size); if(prev && prev->status == B_FREE)
add_to_free_list(heap,(F_FREE_BLOCK *)prev);
} }
/* Compute total sum of sizes of free blocks, and size of largest free block */ /* Compute total sum of sizes of free blocks, and size of largest free block */

View File

@ -1,14 +1,24 @@
#define FREE_LIST_COUNT 16
#define BLOCK_SIZE_INCREMENT 32
typedef struct {
F_FREE_BLOCK *small[FREE_LIST_COUNT];
F_FREE_BLOCK *large;
} F_HEAP_FREE_LIST;
typedef struct { typedef struct {
F_SEGMENT *segment; F_SEGMENT *segment;
F_FREE_BLOCK *free_list; F_HEAP_FREE_LIST free;
} F_HEAP; } F_HEAP;
typedef void (*HEAP_ITERATOR)(F_BLOCK *compiled);
void new_heap(F_HEAP *heap, CELL size); void new_heap(F_HEAP *heap, CELL size);
void build_free_list(F_HEAP *heap, CELL size); void build_free_list(F_HEAP *heap, CELL size);
F_BLOCK *heap_allot(F_HEAP *heap, CELL size); F_BLOCK *heap_allot(F_HEAP *heap, CELL size);
void mark_block(F_BLOCK *block); void mark_block(F_BLOCK *block);
void unmark_marked(F_HEAP *heap); void unmark_marked(F_HEAP *heap);
void free_unmarked(F_HEAP *heap); void free_unmarked(F_HEAP *heap, HEAP_ITERATOR iter);
void heap_usage(F_HEAP *heap, CELL *used, CELL *total_free, CELL *max_free); void heap_usage(F_HEAP *heap, CELL *used, CELL *total_free, CELL *max_free);
CELL heap_size(F_HEAP *heap); CELL heap_size(F_HEAP *heap);
CELL compute_heap_forwarding(F_HEAP *heap); CELL compute_heap_forwarding(F_HEAP *heap);

View File

@ -416,13 +416,6 @@ void end_gc(CELL gc_elapsed)
reset_generations(NURSERY,collecting_gen); reset_generations(NURSERY,collecting_gen);
} }
if(collecting_gen == TENURED)
{
/* now that all reachable code blocks have been marked,
deallocate the rest */
free_unmarked(&code_heap);
}
collecting_aging_again = false; collecting_aging_again = false;
} }
@ -491,7 +484,7 @@ void garbage_collection(CELL gen,
code_heap_scans++; code_heap_scans++;
if(collecting_gen == TENURED) if(collecting_gen == TENURED)
update_code_heap_roots(); free_unmarked(&code_heap,(HEAP_ITERATOR)update_literal_references);
else else
copy_code_heap_roots(); copy_code_heap_roots();