Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
devops committed Jan 22, 2025
2 parents c36e5c3 + c55f039 commit a39de5a
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 67 deletions.
79 changes: 28 additions & 51 deletions include/runtime/arena.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,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) { }

~arena() {
if (current_addr_ptr)
Expand All @@ -36,17 +46,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 @@ -67,6 +77,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 @@ -92,20 +110,6 @@ 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.
Expand All @@ -114,16 +118,13 @@ class arena {
char *allocation_ptr
= nullptr; // next available location in current semispace
char *tripwire = nullptr; // allocating past this triggers slow allocation
mutable size_t num_blocks
= 0; // notional number of BLOCK_SIZE blocks in current semispace
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 @@ -139,10 +140,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 Down Expand Up @@ -183,32 +180,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 @@ -72,9 +72,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

0 comments on commit a39de5a

Please sign in to comment.