More efficient minor GC

db4
Slava Pestov 2008-05-07 21:39:20 -05:00
parent 3ada291e8a
commit 739a75f2bb
6 changed files with 108 additions and 52 deletions

View File

@ -5,6 +5,7 @@ IN: compiler.constants
! These constants must match vm/memory.h ! These constants must match vm/memory.h
: card-bits 6 ; : card-bits 6 ;
: deck-bits 12 ;
: card-mark HEX: 40 HEX: 80 bitor ; : card-mark HEX: 40 HEX: 80 bitor ;
! These constants must match vm/layouts.h ! These constants must match vm/layouts.h

View File

@ -63,9 +63,15 @@ IN: cpu.x86.intrinsics
: generate-write-barrier ( -- ) : generate-write-barrier ( -- )
#! Mark the card pointed to by vreg. #! Mark the card pointed to by vreg.
"val" get operand-immediate? "obj" get fresh-object? or [ "val" get operand-immediate? "obj" get fresh-object? or [
! Mark the card
"obj" operand card-bits SHR "obj" operand card-bits SHR
"cards_offset" f temp-reg v>operand %alien-global "cards_offset" f temp-reg v>operand %alien-global
temp-reg v>operand "obj" operand [+] card-mark OR temp-reg v>operand "obj" operand [+] card-mark OR
! Mark the card deck
"obj" operand deck-bits card-bits - SHR
"decks_offset" f temp-reg v>operand %alien-global
temp-reg v>operand "obj" operand [+] card-mark OR
] unless ; ] unless ;
\ set-slot { \ set-slot {

View File

@ -372,7 +372,7 @@ M: object infer-call
t over set-effect-terminated? t over set-effect-terminated?
set-primitive-effect set-primitive-effect
\ data-room { } { integer array } <effect> set-primitive-effect \ data-room { } { integer integer array } <effect> set-primitive-effect
\ data-room make-flushable \ data-room make-flushable
\ code-room { } { integer integer integer integer } <effect> set-primitive-effect \ code-room { } { integer integer integer integer } <effect> set-primitive-effect

View File

@ -40,10 +40,6 @@ HELP: instances
HELP: gc ( -- ) HELP: gc ( -- )
{ $description "Performs a full garbage collection." } ; { $description "Performs a full garbage collection." } ;
HELP: gc-time ( -- n )
{ $values { "n" "a timestamp in milliseconds" } }
{ $description "Outputs the total time spent in garbage collection during this Factor session." } ;
HELP: data-room ( -- cards generations ) HELP: data-room ( -- cards generations )
{ $values { "cards" "number of bytes reserved for card marking" } { "generations" "array of free/total bytes pairs" } } { $values { "cards" "number of bytes reserved for card marking" } { "generations" "array of free/total bytes pairs" } }
{ $description "Queries the runtime for memory usage information." } ; { $description "Queries the runtime for memory usage information." } ;

View File

@ -21,10 +21,11 @@ CELL init_zone(F_ZONE *z, CELL size, CELL start)
return z->end; return z->end;
} }
void init_cards_offset(void) void init_card_decks(void)
{ {
cards_offset = (CELL)data_heap->cards CELL start = data_heap->segment->start & ~(DECK_SIZE - 1);
- (data_heap->segment->start >> CARD_BITS); cards_offset = (CELL)data_heap->cards - (start >> CARD_BITS);
decks_offset = (CELL)data_heap->decks - (start >> DECK_BITS);
} }
F_DATA_HEAP *alloc_data_heap(CELL gens, F_DATA_HEAP *alloc_data_heap(CELL gens,
@ -62,10 +63,14 @@ F_DATA_HEAP *alloc_data_heap(CELL gens,
data_heap->generations = safe_malloc(sizeof(F_ZONE) * data_heap->gen_count); data_heap->generations = safe_malloc(sizeof(F_ZONE) * data_heap->gen_count);
data_heap->semispaces = safe_malloc(sizeof(F_ZONE) * data_heap->gen_count); data_heap->semispaces = safe_malloc(sizeof(F_ZONE) * data_heap->gen_count);
CELL cards_size = total_size / CARD_SIZE; CELL cards_size = (total_size + DECK_SIZE) / CARD_SIZE;
data_heap->cards = safe_malloc(cards_size); data_heap->cards = safe_malloc(cards_size);
data_heap->cards_end = data_heap->cards + cards_size; data_heap->cards_end = data_heap->cards + cards_size;
CELL decks_size = (total_size + DECK_SIZE) / DECK_SIZE;
data_heap->decks = safe_malloc(decks_size);
data_heap->decks_end = data_heap->decks + decks_size;
CELL alloter = data_heap->segment->start; CELL alloter = data_heap->segment->start;
alloter = init_zone(&data_heap->generations[TENURED],tenured_size,alloter); alloter = init_zone(&data_heap->generations[TENURED],tenured_size,alloter);
@ -105,6 +110,7 @@ void dealloc_data_heap(F_DATA_HEAP *data_heap)
free(data_heap->generations); free(data_heap->generations);
free(data_heap->semispaces); free(data_heap->semispaces);
free(data_heap->cards); free(data_heap->cards);
free(data_heap->decks);
free(data_heap); free(data_heap);
} }
@ -113,17 +119,28 @@ cleared when a generation has been cleared */
void clear_cards(CELL from, CELL to) void clear_cards(CELL from, CELL to)
{ {
/* NOTE: reverse order due to heap layout. */ /* NOTE: reverse order due to heap layout. */
F_CARD *first_card = ADDR_TO_CARD(data_heap->generations[to].start);
F_CARD *last_card = ADDR_TO_CARD(data_heap->generations[from].end); F_CARD *last_card = ADDR_TO_CARD(data_heap->generations[from].end);
F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[to].start); F_CARD *ptr;
for(; ptr < last_card; ptr++) for(ptr = first_card; ptr < last_card; ptr++)
clear_card(ptr); *ptr = CARD_BASE_MASK; /* invalid value */
}
void clear_decks(CELL from, CELL to)
{
/* NOTE: reverse order due to heap layout. */
F_CARD *first_deck = ADDR_TO_CARD(data_heap->generations[to].start);
F_CARD *last_deck = ADDR_TO_CARD(data_heap->generations[from].end);
F_CARD *ptr;
for(ptr = first_deck; ptr < last_deck; ptr++)
*ptr = 0;
} }
void set_data_heap(F_DATA_HEAP *data_heap_) void set_data_heap(F_DATA_HEAP *data_heap_)
{ {
data_heap = data_heap_; data_heap = data_heap_;
nursery = data_heap->generations[NURSERY]; nursery = data_heap->generations[NURSERY];
init_cards_offset(); init_card_decks();
clear_cards(NURSERY,TENURED); clear_cards(NURSERY,TENURED);
} }
@ -141,13 +158,16 @@ void init_data_heap(CELL gens,
extra_roots_region = alloc_segment(getpagesize()); extra_roots_region = alloc_segment(getpagesize());
extra_roots = extra_roots_region->start - CELLS; extra_roots = extra_roots_region->start - CELLS;
gc_time = 0; nursery_gc_time = 0;
aging_collections = 0;
nursery_collections = 0; nursery_collections = 0;
aging_gc_time = 0;
aging_collections = 0;
tenured_gc_time = 0;
tenured_collections = 0; tenured_collections = 0;
cards_checked = 0;
cards_scanned = 0; cards_scanned = 0;
decks_scanned = 0;
code_heap_scans = 0; code_heap_scans = 0;
bytes_copied = 0;
secure_gc = secure_gc_; secure_gc = secure_gc_;
} }
@ -231,6 +251,7 @@ DEFINE_PRIMITIVE(data_room)
int gen; int gen;
dpush(tag_fixnum((data_heap->cards_end - data_heap->cards) >> 10)); dpush(tag_fixnum((data_heap->cards_end - data_heap->cards) >> 10));
dpush(tag_fixnum((data_heap->decks_end - data_heap->decks) >> 10));
for(gen = 0; gen < data_heap->gen_count; gen++) for(gen = 0; gen < data_heap->gen_count; gen++)
{ {
@ -293,9 +314,9 @@ void collect_card(F_CARD *ptr, CELL gen, CELL here)
if(offset == CARD_BASE_MASK) if(offset == CARD_BASE_MASK)
{ {
if(c == 0xff) /* if(c == 0xff)
critical_error("bad card",(CELL)ptr); critical_error("bad card",(CELL)ptr);
else else */
return; return;
} }
@ -308,23 +329,18 @@ void collect_card(F_CARD *ptr, CELL gen, CELL here)
cards_scanned++; cards_scanned++;
} }
void collect_card_deck(CELL gen, void collect_card_deck(F_DECK *deck, CELL gen, F_CARD mask, F_CARD unmask)
F_CARD *first_card, F_CARD *last_card,
F_CARD mask, F_CARD unmask)
{ {
CELL here = data_heap->generations[gen].here; F_CARD *first_card = DECK_TO_CARD(deck);
F_CARD *last_card = DECK_TO_CARD(deck + 1);
long long cards_checked_ = 0; CELL here = data_heap->generations[gen].here;
u32 *quad_ptr; u32 *quad_ptr;
u32 quad_mask = mask | (mask << 8) | (mask << 16) | (mask << 24); u32 quad_mask = mask | (mask << 8) | (mask << 16) | (mask << 24);
u32 *last_card_aligned = (u32 *)(((CELL)last_card + 3) & ~3); for(quad_ptr = (u32 *)first_card; quad_ptr < (u32 *)last_card; quad_ptr++)
for(quad_ptr = (u32 *)first_card; quad_ptr <= (u32 *)last_card_aligned; quad_ptr++)
{ {
cards_checked_ += 4;
if(*quad_ptr & quad_mask) if(*quad_ptr & quad_mask)
{ {
F_CARD *ptr = (F_CARD *)quad_ptr; F_CARD *ptr = (F_CARD *)quad_ptr;
@ -341,14 +357,14 @@ void collect_card_deck(CELL gen,
} }
} }
cards_checked += cards_checked_; decks_scanned++;
} }
/* Copy all newspace objects referenced from marked cards to the destination */ /* Copy all newspace objects referenced from marked cards to the destination */
void collect_gen_cards(CELL gen) void collect_gen_cards(CELL gen)
{ {
F_CARD *first_card = ADDR_TO_CARD(data_heap->generations[gen].start); F_DECK *first_deck = ADDR_TO_DECK(data_heap->generations[gen].start);
F_CARD *last_card = ADDR_TO_CARD(data_heap->generations[gen].here - 1); F_DECK *last_deck = ADDR_TO_DECK(data_heap->generations[gen].end);
F_CARD mask, unmask; F_CARD mask, unmask;
@ -398,7 +414,16 @@ void collect_gen_cards(CELL gen)
return; return;
} }
collect_card_deck(gen,first_card,last_card,mask,unmask); F_DECK *ptr;
for(ptr = first_deck; ptr < last_deck; ptr++)
{
if(*ptr & mask)
{
collect_card_deck(ptr,gen,mask,unmask);
*ptr &= ~unmask;
}
}
} }
/* Scan cards in all generations older than the one being collected, copying /* Scan cards in all generations older than the one being collected, copying
@ -485,6 +510,7 @@ INLINE void *copy_untagged_object(void *pointer, CELL size)
longjmp(gc_jmp,1); longjmp(gc_jmp,1);
allot_barrier(newspace->here); allot_barrier(newspace->here);
newpointer = allot_zone(newspace,size); newpointer = allot_zone(newspace,size);
bytes_copied += size;
memcpy(newpointer,pointer,size); memcpy(newpointer,pointer,size);
return newpointer; return newpointer;
} }
@ -615,6 +641,7 @@ CELL collect_next(CELL scan)
INLINE void reset_generation(CELL i) INLINE void reset_generation(CELL i)
{ {
F_ZONE *z = (i == NURSERY ? &nursery : &data_heap->generations[i]); F_ZONE *z = (i == NURSERY ? &nursery : &data_heap->generations[i]);
bytes_collected += (z->here - z->start);
z->here = z->start; z->here = z->start;
if(secure_gc) if(secure_gc)
memset((void*)z->start,69,z->size); memset((void*)z->start,69,z->size);
@ -669,7 +696,7 @@ void begin_gc(CELL requested_bytes)
#endif #endif
} }
void end_gc(void) void end_gc(CELL gc_elapsed)
{ {
if(growing_data_heap) if(growing_data_heap)
{ {
@ -689,11 +716,13 @@ void end_gc(void)
if(collecting_gen == TENURED) if(collecting_gen == TENURED)
{ {
tenured_collections++; tenured_collections++;
tenured_gc_time += gc_elapsed;
GC_PRINT(END_AGING_GC,aging_collections,cards_scanned); GC_PRINT(END_AGING_GC,aging_collections,cards_scanned);
} }
else if(HAVE_AGING_P && collecting_gen == AGING) else if(HAVE_AGING_P && collecting_gen == AGING)
{ {
aging_collections++; aging_collections++;
aging_gc_time += gc_elapsed;
GC_PRINT(END_NURSERY_GC,nursery_collections,cards_scanned); GC_PRINT(END_NURSERY_GC,nursery_collections,cards_scanned);
} }
} }
@ -704,6 +733,7 @@ void end_gc(void)
reset_generations(NURSERY,collecting_gen); reset_generations(NURSERY,collecting_gen);
nursery_collections++; nursery_collections++;
nursery_gc_time += gc_elapsed;
} }
if(collecting_gen == TENURED) if(collecting_gen == TENURED)
@ -802,9 +832,8 @@ void garbage_collection(CELL gen,
CELL gc_elapsed = (current_millis() - start); CELL gc_elapsed = (current_millis() - start);
GC_PRINT(END_GC,gc_elapsed); GC_PRINT(END_GC,gc_elapsed);
end_gc(); end_gc(gc_elapsed);
gc_time += gc_elapsed;
performing_gc = false; performing_gc = false;
} }
@ -826,15 +855,19 @@ DEFINE_PRIMITIVE(gc)
/* Push total time spent on GC */ /* Push total time spent on GC */
DEFINE_PRIMITIVE(gc_stats) DEFINE_PRIMITIVE(gc_stats)
{ {
CELL array = tag_object(allot_array(ARRAY_TYPE,7,F)); CELL array = tag_object(allot_array(ARRAY_TYPE,11,F));
REGISTER_ROOT(array); REGISTER_ROOT(array);
set_array_nth(untag_object(array),0,tag_bignum(long_long_to_bignum(gc_time))); set_array_nth(untag_object(array),0,allot_cell(nursery_gc_time));
set_array_nth(untag_object(array),1,allot_cell(nursery_collections)); set_array_nth(untag_object(array),1,allot_cell(nursery_collections));
set_array_nth(untag_object(array),2,allot_cell(aging_collections)); set_array_nth(untag_object(array),2,allot_cell(aging_gc_time));
set_array_nth(untag_object(array),3,allot_cell(tenured_collections)); set_array_nth(untag_object(array),3,allot_cell(aging_collections));
set_array_nth(untag_object(array),4,tag_bignum(long_long_to_bignum(cards_scanned))); set_array_nth(untag_object(array),4,allot_cell(tenured_gc_time));
set_array_nth(untag_object(array),5,tag_bignum(long_long_to_bignum(cards_checked))); set_array_nth(untag_object(array),5,allot_cell(tenured_collections));
set_array_nth(untag_object(array),6,allot_cell(code_heap_scans)); set_array_nth(untag_object(array),6,tag_bignum(long_long_to_bignum(cards_scanned)));
set_array_nth(untag_object(array),7,tag_bignum(long_long_to_bignum(decks_scanned)));
set_array_nth(untag_object(array),8,allot_cell(code_heap_scans));
set_array_nth(untag_object(array),9,tag_bignum(long_long_to_bignum(bytes_copied)));
set_array_nth(untag_object(array),10,tag_bignum(long_long_to_bignum(bytes_collected)));
UNREGISTER_ROOT(array); UNREGISTER_ROOT(array);
dpush(array); dpush(array);
} }

View File

@ -46,6 +46,9 @@ typedef struct {
CELL *cards; CELL *cards;
CELL *cards_end; CELL *cards_end;
CELL *decks;
CELL *decks_end;
} F_DATA_HEAP; } F_DATA_HEAP;
F_DATA_HEAP *data_heap; F_DATA_HEAP *data_heap;
@ -71,17 +74,27 @@ offset within the card */
#define CARD_BITS 6 #define CARD_BITS 6
#define ADDR_CARD_MASK (CARD_SIZE-1) #define ADDR_CARD_MASK (CARD_SIZE-1)
INLINE void clear_card(F_CARD *c)
{
*c = CARD_BASE_MASK; /* invalid value */
}
DLLEXPORT CELL cards_offset; DLLEXPORT CELL cards_offset;
void init_cards_offset(void);
#define ADDR_TO_CARD(a) (F_CARD*)(((CELL)(a) >> CARD_BITS) + cards_offset) #define ADDR_TO_CARD(a) (F_CARD*)(((CELL)(a) >> CARD_BITS) + cards_offset)
#define CARD_TO_ADDR(c) (CELL*)(((CELL)(c) - cards_offset)<<CARD_BITS) #define CARD_TO_ADDR(c) (CELL*)(((CELL)(c) - cards_offset)<<CARD_BITS)
/* A deck is 4 kilobytes or 64 cards. */
typedef u8 F_DECK;
#define DECK_SIZE (4 * 1024)
#define DECK_BITS 12
#define ADDR_DECK_MASK (DECK_SIZE-1)
DLLEXPORT CELL decks_offset;
#define ADDR_TO_DECK(a) (F_DECK*)(((CELL)(a) >> DECK_BITS) + decks_offset)
#define DECK_TO_ADDR(c) (CELL*)(((CELL)(c) - decks_offset)<<DECK_BITS)
#define DECK_TO_CARD(d) (F_CARD*)((((CELL)(d) - decks_offset) << (DECK_BITS - CARD_BITS)) + cards_offset)
void init_card_decks(void);
/* this is an inefficient write barrier. compiled definitions use a more /* this is an inefficient write barrier. compiled definitions use a more
efficient one hand-coded in assembly. the write barrier must be called efficient one hand-coded in assembly. the write barrier must be called
any time we are potentially storing a pointer from an older generation any time we are potentially storing a pointer from an older generation
@ -89,7 +102,10 @@ to a younger one */
INLINE void write_barrier(CELL address) INLINE void write_barrier(CELL address)
{ {
F_CARD *c = ADDR_TO_CARD(address); F_CARD *c = ADDR_TO_CARD(address);
*c |= (CARD_POINTS_TO_NURSERY | CARD_POINTS_TO_AGING); *c |= CARD_MARK_MASK;
F_DECK *d = ADDR_TO_DECK(address);
*d = CARD_MARK_MASK ;
} }
#define SLOT(obj,slot) (UNTAG(obj) + (slot) * CELLS) #define SLOT(obj,slot) (UNTAG(obj) + (slot) * CELLS)
@ -142,12 +158,16 @@ void init_data_heap(CELL gens,
bool secure_gc_); bool secure_gc_);
/* statistics */ /* statistics */
s64 gc_time; CELL nursery_gc_time;
CELL nursery_collections; CELL nursery_collections;
CELL aging_gc_time;
CELL aging_collections; CELL aging_collections;
CELL tenured_gc_time;
CELL tenured_collections; CELL tenured_collections;
s64 cards_checked; u64 cards_scanned;
s64 cards_scanned; u64 decks_scanned;
u64 bytes_copied;
u64 bytes_collected;
CELL code_heap_scans; CELL code_heap_scans;
/* only meaningful during a GC */ /* only meaningful during a GC */