Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Third attempt at refactoring garbage collector #1184

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 30 additions & 53 deletions include/runtime/arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -132,10 +133,6 @@ inline char arena::get_arena_semispace_id_of_object(void *ptr) {
return *reinterpret_cast<char *>(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
Expand All @@ -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;
}
Expand All @@ -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();
}
}
Expand Down
8 changes: 4 additions & 4 deletions runtime/alloc/arena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
13 changes: 7 additions & 6 deletions runtime/collect/collect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -295,21 +295,21 @@ 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
for (int i = 0; i < 2048; i++) {
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);
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down
22 changes: 16 additions & 6 deletions runtime/lto/alloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions runtime/main/main.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions runtime/util/finish_rewriting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

extern "C" {

char *input_filename;
FILE *output_file = nullptr;
void *proof_writer = nullptr;
bool statistics = false;
Expand Down
Loading