diff --git a/include/runtime/arena.h b/include/runtime/arena.h index 4bb5ac315..c9c869271 100644 --- a/include/runtime/arena.h +++ b/include/runtime/arena.h @@ -13,12 +13,22 @@ extern "C" { size_t const HYPERBLOCK_SIZE = (size_t)BLOCK_SIZE * 1024 * 1024; +// After a garbage collect we change the tripwire to the amount of non-garbage times +// this factor, so we do a decent amount of allocations between collections even +// when there is very little garbage +size_t const EXPAND_FACTOR = 2; + +// We don't consider collecting garbage until at least this amount of space has +// been allocated, to avoid collections near startup when there is little garbage. +size_t const MIN_SPACE = 1024 * 1024; + // An arena can be used to allocate objects that can then be deallocated all at // once. class arena { public: - arena(char id) - : allocation_semispace_id(id) { + arena(char id, bool trigger_collection) + : allocation_semispace_id(id) + , trigger_collection(trigger_collection) { initialize_semispace(); } @@ -30,17 +40,17 @@ class arena { // Returns the address of the first byte that belongs in the given arena. // Returns nullptr if nothing has been allocated ever in that arena. - char *arena_start_ptr() const { return current_addr_ptr; } + char *start_ptr() const { return current_addr_ptr; } // Returns a pointer to a location holding the address of last allocated // byte in the given arena plus 1. // This address is nullptr if nothing has been allocated ever in that arena. - char *arena_end_ptr() { return allocation_ptr; } + char *end_ptr() { return allocation_ptr; } // Clears the current allocation space by setting its start back to its first // block. It is used during garbage collection to effectively collect all of the - // arena. Resets the tripwire. - void arena_clear(); + // arena. + void arena_clear() { allocation_ptr = current_addr_ptr; } // Resizes the last allocation. // Returns the address of the byte following the last newly allocated byte. @@ -61,6 +71,14 @@ class arena { // It is used before garbage collection. void arena_swap_and_clear(); + // Decide how much space to use in arena before setting the flag for a collection. + // If an arena is going to request collections, updating this at the end of a + // collection is mandatory. + void update_tripwire() { + size_t space = EXPAND_FACTOR * (allocation_ptr - current_addr_ptr); + tripwire = current_addr_ptr + ((space < MIN_SPACE) ? MIN_SPACE : space); + } + // Given two pointers to objects allocated in the same arena, return the number // of bytes they are apart. Undefined behavior will result if the pointers // don't belong to the same arena @@ -86,37 +104,20 @@ class arena { static char get_arena_semispace_id_of_object(void *ptr); private: - // - // We update the number of 1MB blocks actually written to, only when we need this value, - // or before a garbage collection rather than trying to determine when we write to a fresh block. - // - void update_num_blocks() const { - // - // Calculate how many 1M blocks of the current arena we used. - // - size_t num_used_blocks - = (allocation_ptr - current_addr_ptr - 1) / BLOCK_SIZE + 1; - if (num_used_blocks > num_blocks) - num_blocks = num_used_blocks; - } - void initialize_semispace(); // // Current semispace where allocations are being made. // char *current_addr_ptr; // pointer to start of current address space char *allocation_ptr; // next available location in current semispace - char *tripwire; // allocating past this triggers slow allocation - mutable size_t - num_blocks; // notional number of BLOCK_SIZE blocks in current semispace + char *tripwire; // allocating past this sets flag for collection char allocation_semispace_id; // id of current semispace + bool const trigger_collection; // request collections? // // Semispace where allocations will be made during and after garbage collect. // char *collection_addr_ptr = nullptr; // pointer to start of collection address space - size_t num_collection_blocks - = 0; // notional number of BLOCK_SIZE blocks in collection semispace }; inline char arena::get_arena_semispace_id_of_object(void *ptr) { @@ -132,10 +133,6 @@ inline char arena::get_arena_semispace_id_of_object(void *ptr) { return *reinterpret_cast(end_address); } -// Macro to define a new arena with the given ID. Supports IDs ranging from 0 to -// 127. -#define REGISTER_ARENA(name, id) thread_local arena name(id) - #ifdef __MACH__ // // thread_local disabled for Apple @@ -157,7 +154,7 @@ inline void *arena::kore_arena_alloc(size_t requested) { time_for_collection = true; // // We move the tripwire to 1 past the end of our hyperblock so that we have - // a well defined comparison that will always be false until the next arena swap. + // a well defined comparison that will always be false update_tripwire() is called. // tripwire = current_addr_ptr + HYPERBLOCK_SIZE; } @@ -169,32 +166,12 @@ inline void *arena::kore_arena_alloc(size_t requested) { return result; } -inline void arena::arena_clear() { - // - // We set the allocation pointer to the first available address. - // - allocation_ptr = arena_start_ptr(); - // - // If the number of blocks we've touched is >= threshold, we want to trigger - // a garbage collection if we get within 1 block of the end of this area. - // Otherwise we only want to generate a garbage collect if we allocate off the - // end of this area. - // - tripwire = current_addr_ptr - + (num_blocks - (num_blocks >= get_gc_threshold())) * BLOCK_SIZE; -} - inline void arena::arena_swap_and_clear() { - update_num_blocks(); // so we save the correct number of touched blocks std::swap(current_addr_ptr, collection_addr_ptr); - std::swap(num_blocks, num_collection_blocks); allocation_semispace_id = ~allocation_semispace_id; - if (current_addr_ptr == nullptr) { - // - // The other semispace hasn't be initialized yet. - // - initialize_semispace(); - } else + if (current_addr_ptr == nullptr) + initialize_semispace(); // not yet initialized + else arena_clear(); } } diff --git a/runtime/alloc/arena.cpp b/runtime/alloc/arena.cpp index d10bcaad9..871b2efef 100644 --- a/runtime/alloc/arena.cpp +++ b/runtime/alloc/arena.cpp @@ -56,9 +56,9 @@ void arena::initialize_semispace() { current_addr_ptr[HYPERBLOCK_SIZE - 1] = allocation_semispace_id; allocation_ptr = current_addr_ptr; // - // We set the tripwire for this space so we get trigger a garbage collection when we pass BLOCK_SIZE of memory - // allocated from this space. + // If we're set to trigger garbage collections, set the tripwire for MIN_SPACE of allocations otherwise + // set it out-of-bounds (but still legal for comparison). // - tripwire = current_addr_ptr + BLOCK_SIZE; - num_blocks = 1; + tripwire + = current_addr_ptr + (trigger_collection ? MIN_SPACE : HYPERBLOCK_SIZE); } diff --git a/runtime/collect/collect.cpp b/runtime/collect/collect.cpp index f49123950..981b29955 100644 --- a/runtime/collect/collect.cpp +++ b/runtime/collect/collect.cpp @@ -255,7 +255,7 @@ char *arena::evacuate(char *scan_ptr) { migrate_child(curr_block, layout_data->args, i, false); } } - return move_ptr(scan_ptr, get_size(hdr, layout_int), arena_end_ptr()); + return move_ptr(scan_ptr, get_size(hdr, layout_int), end_ptr()); } // Contains the decision logic for collecting the old generation. @@ -295,7 +295,7 @@ void kore_collect( if (!last_alloc_ptr) { last_alloc_ptr = youngspace_ptr(); } - char *current_alloc_ptr = youngspace.arena_end_ptr(); + char *current_alloc_ptr = youngspace.end_ptr(); #endif kore_alloc_swap(collect_old); #ifdef GC_DBG @@ -303,13 +303,13 @@ void kore_collect( numBytesLiveAtCollection[i] = 0; } #endif - char *previous_oldspace_alloc_ptr = oldspace.arena_end_ptr(); + char *previous_oldspace_alloc_ptr = oldspace.end_ptr(); for (int i = 0; i < nroots; i++) { migrate_root(roots, type_info, i); } migrate_static_roots(); char *scan_ptr = youngspace_ptr(); - if (scan_ptr != youngspace.arena_end_ptr()) { + if (scan_ptr != youngspace.end_ptr()) { MEM_LOG("Evacuating young generation\n"); while (scan_ptr) { scan_ptr = youngspace.evacuate(scan_ptr); @@ -320,7 +320,7 @@ void kore_collect( } else { scan_ptr = previous_oldspace_alloc_ptr; } - if (scan_ptr != oldspace.arena_end_ptr()) { + if (scan_ptr != oldspace.end_ptr()) { MEM_LOG("Evacuating old generation\n"); while (scan_ptr) { scan_ptr = oldspace.evacuate(scan_ptr); @@ -331,12 +331,13 @@ void kore_collect( = arena::ptr_diff(current_alloc_ptr, last_alloc_ptr); assert(numBytesAllocedSinceLastCollection >= 0); fwrite(&numBytesAllocedSinceLastCollection, sizeof(ssize_t), 1, stderr); - last_alloc_ptr = youngspace.arena_end_ptr(); + last_alloc_ptr = youngspace.end_ptr(); fwrite( numBytesLiveAtCollection, sizeof(numBytesLiveAtCollection[0]), sizeof(numBytesLiveAtCollection) / sizeof(numBytesLiveAtCollection[0]), stderr); #endif + youngspace.update_tripwire(); MEM_LOG("Finishing garbage collection\n"); is_gc = false; } diff --git a/runtime/lto/alloc.cpp b/runtime/lto/alloc.cpp index aaafc932d..e0d7a010b 100644 --- a/runtime/lto/alloc.cpp +++ b/runtime/lto/alloc.cpp @@ -11,16 +11,26 @@ extern "C" { -REGISTER_ARENA(youngspace, YOUNGSPACE_ID); -REGISTER_ARENA(oldspace, OLDSPACE_ID); -REGISTER_ARENA(alwaysgcspace, ALWAYSGCSPACE_ID); +// class arena supports ID from 0 to 127 + +// New data in allocated in the youngspace, which requests a +// collection when is gets too full. +thread_local arena youngspace(YOUNGSPACE_ID, true); + +// Data that is old enough is migrated to the oldspace. The +// migrated data is always live at this point so it never +// requests a collection. +thread_local arena oldspace(OLDSPACE_ID, false); + +// Temporary data is doesn't use the garbage collector. +thread_local arena alwaysgcspace(ALWAYSGCSPACE_ID, false); char *youngspace_ptr() { - return youngspace.arena_start_ptr(); + return youngspace.start_ptr(); } char *oldspace_ptr() { - return oldspace.arena_start_ptr(); + return oldspace.start_ptr(); } char youngspace_collection_id() { @@ -73,7 +83,7 @@ kore_resize_last_alloc(void *oldptr, size_t newrequest, size_t last_size) { newrequest = (newrequest + 7) & ~7; last_size = (last_size + 7) & ~7; - if (oldptr != youngspace.arena_end_ptr() - last_size) { + if (oldptr != youngspace.end_ptr() - last_size) { MEM_LOG( "May only reallocate last allocation. Tried to reallocate %p to %zd\n", oldptr, newrequest); diff --git a/runtime/main/main.ll b/runtime/main/main.ll index a81216c74..c258a9d5a 100644 --- a/runtime/main/main.ll +++ b/runtime/main/main.ll @@ -21,6 +21,7 @@ declare void @print_proof_hint_header(ptr) @proof_out.flag = private constant [15 x i8] c"--proof-output\00" @proof_chunk.flag = private constant [19 x i8] c"--proof-chunk-size\00" +@input_filename = external global ptr @proof_writer = external global ptr @statistics = external global i1 @binary_output = external global i1 @@ -119,6 +120,7 @@ if: call void @print_proof_hint_header(ptr %proof_writer) br label %else else: + store ptr %filename, ptr @input_filename %ret = call ptr @parse_configuration(ptr %filename) %result = call ptr @take_steps(i64 %depth, ptr %ret) call void @finish_rewriting(ptr %result, i1 0) diff --git a/runtime/util/finish_rewriting.cpp b/runtime/util/finish_rewriting.cpp index cc18dfe0d..bedc2a477 100644 --- a/runtime/util/finish_rewriting.cpp +++ b/runtime/util/finish_rewriting.cpp @@ -9,6 +9,7 @@ extern "C" { +char *input_filename; FILE *output_file = nullptr; void *proof_writer = nullptr; bool statistics = false;