Skip to content

Commit

Permalink
Add memory limits to gc tests (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
Giselus authored Jan 20, 2025
1 parent 59379c1 commit 38deacf
Show file tree
Hide file tree
Showing 30 changed files with 113 additions and 58 deletions.
1 change: 0 additions & 1 deletion examples/gc/allocating-in-nested-functions/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/allocating-in-nested-functions/program.cac
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# 10000 nested function calls, each of which allocates 128B on the heap

foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 16 * 1024;

let struct_128 = {
a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8,
i=9, j=10, k=11, l=12, m=13, n=14, o=15, p=16
};

let allocateRecursively = [depth: Int] -> Int => (
cassert[(get_current_heap_space[] <= memoryLimit)];
if (depth == 0) then return 0
else (
let newPtr = $struct_128;
Expand Down
1 change: 0 additions & 1 deletion examples/gc/allocation-in-loop/memlimit.txt

This file was deleted.

5 changes: 0 additions & 5 deletions examples/gc/allocation-in-loop/program.cac

This file was deleted.

1 change: 0 additions & 1 deletion examples/gc/allocation-of-structures/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/allocation-of-structures/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 60 * 1024;

let struct_128 = {
a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8,
i=9, j=10, k=11, l=12, m=13, n=14, o=15, p=16
Expand All @@ -10,6 +15,7 @@ let struct_512 = {
let i = 0;
let ref = $struct_512;
while i < 10000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
ref = $struct_512;
@ref;
i=i+1;
Expand Down
1 change: 0 additions & 1 deletion examples/gc/assignment-to-reference-in-loop/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/assignment-to-reference-in-loop/program.cac
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let i = 0;
let ref: &Int = $1;
while i < 1000000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
ref = $1; # reassigning pointer should mark memory as unreachable
i=i+1;
);
Expand Down
1 change: 0 additions & 1 deletion examples/gc/different-liveness/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/different-liveness/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let ref1 = $1;
let ref2 = $1;
let ref3 = $1;
Expand All @@ -9,6 +14,7 @@ let ref8 = $1;

let i = 0;
while i < 1000000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
ref1 = $1;
if i%4 == 0 then (ref2 = $1;);
if i%4 == 1 then (ref3 = $1;);
Expand Down
1 change: 0 additions & 1 deletion examples/gc/multiple-references/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/multiple-references/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 16 * 1024;

let struct_128 = {
a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8,
i=9, j=10, k=11, l=12, m=13, n=14, o=15, p=16
Expand All @@ -12,6 +17,7 @@ let ptr5 = $struct_128;
let i = 0;

while i < 10000 do ( # we are counting number of references to each segment of memory:
cassert[(get_current_heap_space[] <= memoryLimit)];
let temp1 = ptr1; # (r1=2), r2=1, r3=1, r4=1, r5=1
let temp2 = ptr2; # r1=2, (r2=2), r3=1, r4=1, r5=1
ptr2 = $struct_128; # r1=2, r2=1, r3=1, r4=1, r5=1, (r6=1)
Expand Down
1 change: 0 additions & 1 deletion examples/gc/nested-references/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/nested-references/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let struct_32 = {
a=1, b=2, c=3, d=4
};
Expand All @@ -7,6 +12,7 @@ let nested_ref = $$$$$$$$$$struct_32;
let i = 0;

while i < 100000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
nested_ref=$$$$$$$$$$struct_32; # reassigning of pointer should free the memory at its subfields
@@@nested_ref=$$$$$$$struct_32;
@@@@@@nested_ref=$$$$struct_32;
Expand Down
1 change: 0 additions & 1 deletion examples/gc/overlapping-frames/memlimit.txt

This file was deleted.

13 changes: 12 additions & 1 deletion examples/gc/overlapping-frames/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

foreign randint : [Int, Int] -> Int;
foreign cassert : [Bool] -> Unit;

Expand Down Expand Up @@ -25,7 +30,10 @@ let g = [a: &Int, b: &Int] -> Int => (
);

let i = 0;
let a = $1;
let b = $1;
while i < 10000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
i = i + 1;
let a = $randint[100, 110];
let b = $randint[0, 1000];
Expand All @@ -36,4 +44,7 @@ while i < 10000 do (
let should_be_large = g[a, b];
cassert[(should_be_large >= 100)];
);
);
);

i = @a;
i = @b;
1 change: 0 additions & 1 deletion examples/gc/recursively-allocating-structs/memlimit.txt

This file was deleted.

7 changes: 7 additions & 0 deletions examples/gc/recursively-allocating-structs/program.cac
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
# 1000 nested function calls (in 2 variants), each of which allocates 128B on the heap
# each call uses info allocated on a child call

foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let struct_64 = {
a=$1, b=$2, c=$3, d=$4, e=$5, f=$6, g=$7, h=$8
};

let allocateAndIncrement = [depth: Int] -> {
a:&Int, b:&Int, c:&Int, d:&Int, e:&Int, f:&Int, g:&Int, h:&Int
} => (
cassert[(get_current_heap_space[] <= memoryLimit)];
if (depth == 0) then struct_64
else (
let previousStr = allocateAndIncrement[depth - 1];
Expand All @@ -32,6 +38,7 @@ let allocateAndCopy = [depth: Int, ptr: &{
}] -> &{
a:&Int, b:&Int, c:&Int, d:&Int, e:&Int, f:&Int, g:&Int, h:&Int
} => (
cassert[(get_current_heap_space[] <= memoryLimit)];
if (depth == 0) then ptr
else (
let previousPtr = allocateAndCopy[depth - 1, ptr];
Expand Down
1 change: 0 additions & 1 deletion examples/gc/references-in-fields/memlimit.txt

This file was deleted.

7 changes: 7 additions & 0 deletions examples/gc/references-in-fields/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let struct_32 = {
a=1, b=2, c=3, d=4
};
Expand All @@ -13,6 +18,7 @@ let struct_of_ref = {
let i = 0;

while i < 100000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
struct_of_ref.r1 = $struct_32;
struct_of_ref.r2 = $struct_32;
struct_of_ref.r3 = $struct_32;
Expand All @@ -22,6 +28,7 @@ while i < 100000 do (
);

while i < 100000 do ( # we should also free memory when reassigning whole struct
cassert[(get_current_heap_space[] <= memoryLimit)];
struct_of_ref = {r1=$struct_32, r2=$struct_32, r3=$struct_32, r4=$struct_32, r5=$struct_32};
i = i+1;
);
Expand Down
1 change: 0 additions & 1 deletion examples/gc/simple-allocation-in-function/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/simple-allocation-in-function/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let empty = []->Unit=>();

let f = [a: Int] -> Unit => (
Expand All @@ -8,6 +13,7 @@ let f = [a: Int] -> Unit => (

let i : Int = 0;
while (i < 1000000) do (
cassert[(get_current_heap_space[] <= memoryLimit)];
f[i];
i = i + 1;
);
1 change: 0 additions & 1 deletion examples/gc/simple/memlimit.txt

This file was deleted.

8 changes: 6 additions & 2 deletions examples/gc/simple/program.cac
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let x = $1;

# this program is made te ensure that memory limit is enough to run
# code with (almost) no allocations
cassert[(get_current_heap_space[] <= memoryLimit)];
1 change: 0 additions & 1 deletion examples/gc/stack-references/memlimit.txt

This file was deleted.

6 changes: 6 additions & 0 deletions examples/gc/stack-references/program.cac
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
foreign get_current_heap_space: [] -> Int;
foreign cassert : [Bool] -> Unit;

let memoryLimit = 4 * 1024;

let struct_16 = {
a=1, b=2
};
Expand All @@ -17,6 +22,7 @@ let ptr9 = $struct_16;
let i = 0;

while i < 100000 do (
cassert[(get_current_heap_space[] <= memoryLimit)];
ptr0 = $struct_16;
ptr1 = $struct_16;
ptr2 = $struct_16;
Expand Down
47 changes: 26 additions & 21 deletions lib_cacophony/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#endif

static ll getObjectSize(ll *outline) {
return 8 * (1 + *outline);
return sizeof(ll*) * (1 + *outline);
}

void memoryManager::createNewPage(int size) {
Expand All @@ -19,6 +19,17 @@ void memoryManager::createNewPage(int size) {
ll *ptr = static_cast<ll*>(malloc(size));
auto page = memoryPage{size, 0, ptr};
allocated_pages.push_back(page);
occupied_memory += size;
}

// This step is performed during cleanup, where allocated_pages is being modified, therefore no need to modify it here.
void memoryManager::deallocatePage(memoryPage page) {
occupied_memory -= page.size;
free(page.ptr);
}

ll memoryManager::getOccupiedMemory() {
return occupied_memory;
}

ll* memoryManager::allocateMemory(int size) {
Expand Down Expand Up @@ -55,21 +66,18 @@ std::unordered_map<ll*, ll*> memoryManager::cleanup(std::set<ll*> &alive_objects
std::swap(pages_to_process, allocated_pages);
std::set<ll*> processed_pages;

auto traversePage = [&](memoryPage page, auto && handler /* action to perform on each object */) {
for (auto it = page.ptr + 1; it < page.ptr + page.occupied / sizeof(ll*); ) {
handler(it);
ll *outline = reinterpret_cast<ll*>(*(it - 1));
it += (*outline + 1);
auto traverseAliveObjectsOnPage = [&](memoryPage page, auto && handler /* action to perform on each object */) {
for (auto it = alive_objects.lower_bound(page.ptr+1); it != alive_objects.end() && *it < page.ptr + page.size / sizeof(ll*); it++) {
handler(*it);
}

};
for (auto &page : pages_to_process) {
bool is_page_untouched = true;
traversePage(page, [&](ll* ptr) {
if (alive_objects.find(ptr) == alive_objects.end())
is_page_untouched = false;
int alive_memory_size = 0;
traverseAliveObjectsOnPage(page, [&](ll* ptr) {
ll *outline = reinterpret_cast<ll*>(*(ptr - 1));
alive_memory_size += getObjectSize(outline);
});
if (is_page_untouched) {
if (alive_memory_size == page.occupied) {
allocated_pages.push_back(page);
processed_pages.insert(page.ptr);
}
Expand All @@ -80,16 +88,15 @@ std::unordered_map<ll*, ll*> memoryManager::cleanup(std::set<ll*> &alive_objects
continue;

std::vector<ll*> alive_objects_on_page;
traversePage(page, [&](ll* ptr) {
if (alive_objects.find(ptr) != alive_objects.end())
alive_objects_on_page.push_back(ptr);
traverseAliveObjectsOnPage(page, [&](ll* ptr) {
alive_objects_on_page.push_back(ptr);
});
if (alive_objects_on_page.empty()) {
// Deallocate empty page or store it for later usage
if (free_page == nullptr)
free_page = &page;
else
free(page.ptr);
deallocatePage(page);
continue;
}
if(allocated_pages.empty()) {
Expand All @@ -99,9 +106,7 @@ std::unordered_map<ll*, ll*> memoryManager::cleanup(std::set<ll*> &alive_objects
} else
createNewPage(MEMORY_BLOCK_SIZE);
}
traversePage(page, [&](ll* ptr) {
if (alive_objects.find(ptr) == alive_objects.end())
return;
traverseAliveObjectsOnPage(page, [&](ll* ptr) {
ll *outline = reinterpret_cast<ll*>(*(ptr - 1));
int size = getObjectSize(outline);
memoryPage *last_page = nullptr;
Expand All @@ -123,10 +128,10 @@ std::unordered_map<ll*, ll*> memoryManager::cleanup(std::set<ll*> &alive_objects
if (free_page == nullptr)
free_page = &page;
else
free(page.ptr);
deallocatePage(page);
}
if (free_page != nullptr)
free(free_page->ptr);
deallocatePage(*free_page);
// Last page can be empty, but we don't free it, as it may be useful in next cleanup to have clean page to copy into.

if(LOG_GC) {
Expand Down
4 changes: 4 additions & 0 deletions lib_cacophony/gc/gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ extern "C" {
memset(object_ptr, 0, 8 * **(object_ptr - 1));
return object_ptr;
}

ll get_current_heap_space() {
return memory_manager.getOccupiedMemory();
}
}

#endif /* GC_LIB_H */
Loading

0 comments on commit 38deacf

Please sign in to comment.