From ed1ee19ce14ea472bad93e3039879092e524c7b4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 6 Oct 2009 04:36:34 -0500 Subject: [PATCH] vm: add a remembered set for code blocks which may reference young literals. Improves loading time --- vm/code_block.cpp | 11 ++---- vm/code_heap.cpp | 12 +++++++ vm/code_heap.hpp | 14 +++++--- vm/data_gc.cpp | 84 +++++++++++++++++++-------------------------- vm/data_gc.hpp | 3 -- vm/errors.cpp | 2 +- vm/inline_cache.cpp | 2 +- vm/layouts.hpp | 2 +- 8 files changed, 61 insertions(+), 69 deletions(-) diff --git a/vm/code_block.cpp b/vm/code_block.cpp index ec7da8af9f..e5dd57b05f 100755 --- a/vm/code_block.cpp +++ b/vm/code_block.cpp @@ -341,7 +341,7 @@ void factor_vm::update_word_references(code_block *compiled) the code heap with dead PICs that will be freed on the next GC, we add them to the free list immediately. */ else if(compiled->type == PIC_TYPE) - code->heap_free(compiled); + code->code_heap_free(compiled); else { word_references_updater updater(this); @@ -372,18 +372,12 @@ struct code_block_relocator { /* Perform all fixups on a code block */ void factor_vm::relocate_code_block(code_block *compiled) { - compiled->last_scan = data->nursery(); compiled->needs_fixup = false; code_block_relocator relocator(this); iterate_relocations(compiled,relocator); flush_icache_for(compiled); } -void relocate_code_block(code_block *compiled, factor_vm *myvm) -{ - return myvm->relocate_code_block(compiled); -} - /* Fixup labels. This is done at compile time, not image load time */ void factor_vm::fixup_labels(array *labels, code_block *compiled) { @@ -443,7 +437,6 @@ code_block *factor_vm::add_code_block(cell type, cell code_, cell labels_, cell /* compiled header */ compiled->type = type; - compiled->last_scan = data->nursery(); compiled->needs_fixup = true; /* slight space optimization */ @@ -466,7 +459,7 @@ code_block *factor_vm::add_code_block(cell type, cell code_, cell labels_, cell /* next time we do a minor GC, we have to scan the code heap for literals */ - this->code->last_code_heap_scan = data->nursery(); + this->code->write_barrier(compiled); return compiled; } diff --git a/vm/code_heap.cpp b/vm/code_heap.cpp index a0ad1a7dd0..305d8d1e60 100755 --- a/vm/code_heap.cpp +++ b/vm/code_heap.cpp @@ -5,6 +5,18 @@ namespace factor code_heap::code_heap(factor_vm *myvm, cell size) : heap(myvm,size) {} +void code_heap::write_barrier(code_block *compiled) +{ + remembered_set[compiled] = myvm->data->nursery(); + youngest_referenced_generation = myvm->data->nursery(); +} + +void code_heap::code_heap_free(code_block *compiled) +{ + remembered_set.erase(compiled); + heap_free(compiled); +} + /* Allocate a code heap during startup */ void factor_vm::init_code_heap(cell size) { diff --git a/vm/code_heap.hpp b/vm/code_heap.hpp index c0b6d9d1cf..0ccc4ee798 100755 --- a/vm/code_heap.hpp +++ b/vm/code_heap.hpp @@ -2,13 +2,17 @@ namespace factor { struct code_heap : heap { - /* What generation was being collected when trace_code_heap_roots() was last - called? Until the next call to add_code_block(), future - collections of younger generations don't have to touch the code - heap. */ - cell last_code_heap_scan; + /* Maps code blocks to the youngest generation containing + one of their literals. If this is tenured (0), the code block + is not part of the remembered set. */ + unordered_map remembered_set; + /* Minimum value in the above map. */ + cell youngest_referenced_generation; + explicit code_heap(factor_vm *myvm, cell size); + void write_barrier(code_block *compiled); + void code_heap_free(code_block *compiled); }; } diff --git a/vm/data_gc.cpp b/vm/data_gc.cpp index 19a740ac70..748329bfc2 100755 --- a/vm/data_gc.cpp +++ b/vm/data_gc.cpp @@ -5,7 +5,7 @@ namespace factor void factor_vm::init_data_gc() { - code->last_code_heap_scan = data->nursery(); + code->youngest_referenced_generation = data->nursery(); } gc_state::gc_state(data_heap *data_, bool growing_data_heap_, cell collecting_gen_) : @@ -287,47 +287,23 @@ template void factor_vm::trace_contexts(Strategy &strategy) /* Trace all literals referenced from a code block. Only for aging and nursery collections */ template void factor_vm::trace_literal_references(code_block *compiled, Strategy &strategy) { - if(current_gc->collecting_gen >= compiled->last_scan) - { - if(current_gc->collecting_accumulation_gen_p()) - compiled->last_scan = current_gc->collecting_gen; - else - compiled->last_scan = current_gc->collecting_gen + 1; - - trace_handle(&compiled->literals,strategy); - trace_handle(&compiled->relocation,strategy); - - /* once we finish tracing, re-visit this code block and update - literals */ - current_gc->dirty_code_blocks.insert(compiled); - } + trace_handle(&compiled->literals,strategy); + trace_handle(&compiled->relocation,strategy); } -template struct literal_reference_tracer { - factor_vm *myvm; - Strategy strategy; - - explicit literal_reference_tracer(factor_vm *myvm_, Strategy &strategy_) : - myvm(myvm_), strategy(strategy_) {} - - void operator()(code_block *compiled) - { - myvm->trace_literal_references(compiled,strategy); - } -}; - /* Trace literals referenced from all code blocks. Only for aging and nursery collections */ template void factor_vm::trace_code_heap_roots(Strategy &strategy) { - if(current_gc->collecting_gen >= code->last_code_heap_scan) + if(current_gc->collecting_gen >= code->youngest_referenced_generation) { - literal_reference_tracer tracer(this,strategy); - iterate_code_heap(tracer); + unordered_map::const_iterator iter = code->remembered_set.begin(); + unordered_map::const_iterator end = code->remembered_set.end(); - if(current_gc->collecting_accumulation_gen_p()) - code->last_code_heap_scan = current_gc->collecting_gen; - else - code->last_code_heap_scan = current_gc->collecting_gen + 1; + for(; iter != end; iter++) + { + if(current_gc->collecting_gen >= iter->second) + trace_literal_references(iter->first,strategy); + } code_heap_scans++; } @@ -340,9 +316,7 @@ template void factor_vm::mark_code_block(code_block *compiled check_code_address((cell)compiled); code->mark_block(compiled); - - trace_handle(&compiled->literals,strategy); - trace_handle(&compiled->relocation,strategy); + trace_literal_references(compiled,strategy); } struct literal_and_word_reference_updater { @@ -362,19 +336,34 @@ void factor_vm::free_unmarked_code_blocks() { literal_and_word_reference_updater updater(this); code->free_unmarked(updater); - code->last_code_heap_scan = current_gc->collecting_gen; + code->remembered_set.clear(); + code->youngest_referenced_generation = data->tenured(); } void factor_vm::update_dirty_code_blocks() { - std::set dirty_code_blocks = current_gc->dirty_code_blocks; - std::set::const_iterator iter = dirty_code_blocks.begin(); - std::set::const_iterator end = dirty_code_blocks.end(); + /* The youngest generation that any code block can now reference */ + cell gen; + + if(current_gc->collecting_accumulation_gen_p()) + gen = current_gc->collecting_gen; + else + gen = current_gc->collecting_gen + 1; + + unordered_map::iterator iter = code->remembered_set.begin(); + unordered_map::iterator end = code->remembered_set.end(); for(; iter != end; iter++) - update_literal_references(*iter); + { + if(current_gc->collecting_gen >= iter->second) + { + check_code_address((cell)iter->first); + update_literal_references(iter->first); + iter->second = gen; + } + } - dirty_code_blocks.clear(); + code->youngest_referenced_generation = gen; } template @@ -635,11 +624,8 @@ If growing_data_heap_ is true, we must grow the data heap to such a size that an allocation of requested_bytes won't fail */ void factor_vm::garbage_collection(cell collecting_gen_, bool growing_data_heap_, bool trace_contexts_, cell requested_bytes) { - if(gc_off) - { - critical_error("GC disabled",collecting_gen_); - return; - } + assert(!gc_off); + assert(!current_gc); current_gc = new gc_state(data,growing_data_heap_,collecting_gen_); diff --git a/vm/data_gc.hpp b/vm/data_gc.hpp index 80c7cd9a01..a5622ffd1e 100755 --- a/vm/data_gc.hpp +++ b/vm/data_gc.hpp @@ -25,9 +25,6 @@ struct gc_state { full, we go on to collect tenured */ bool collecting_aging_again; - /* A set of code blocks which need to have their literals updated */ - std::set dirty_code_blocks; - /* GC start time, for benchmarking */ u64 start_time; diff --git a/vm/errors.cpp b/vm/errors.cpp index 205733bf94..2435ac1c33 100755 --- a/vm/errors.cpp +++ b/vm/errors.cpp @@ -29,7 +29,7 @@ void factor_vm::throw_error(cell error, stack_frame *callstack_top) { /* If the error handler is set, we rewind any C stack frames and pass the error to user-space. */ - if(userenv[BREAK_ENV] != F) + if(!current_gc && userenv[BREAK_ENV] != F) { /* If error was thrown during heap scan, we re-enable the GC */ gc_off = false; diff --git a/vm/inline_cache.cpp b/vm/inline_cache.cpp index 1626af1965..1369714730 100755 --- a/vm/inline_cache.cpp +++ b/vm/inline_cache.cpp @@ -28,7 +28,7 @@ void factor_vm::deallocate_inline_cache(cell return_address) #endif if(old_type == PIC_TYPE) - code->heap_free(old_block); + code->code_heap_free(old_block); } /* Figure out what kind of type check the PIC needs based on the methods diff --git a/vm/layouts.hpp b/vm/layouts.hpp index 9357e927fb..0208fbd22e 100644 --- a/vm/layouts.hpp +++ b/vm/layouts.hpp @@ -207,7 +207,7 @@ struct heap_block { unsigned char status; /* free or allocated? */ unsigned char type; /* this is WORD_TYPE or QUOTATION_TYPE */ - unsigned char last_scan; /* the youngest generation in which this block's literals may live */ + unsigned char unused; unsigned char needs_fixup; /* is this a new block that needs full fixup? */ /* In bytes, includes this header */