mirror of
https://github.com/ProgramSnail/Lama.git
synced 2025-12-06 06:48:48 +00:00
implemented non-recursive mark phase, added time measurement for stress tests
This commit is contained in:
parent
5da89d0c2e
commit
9107a26668
3 changed files with 91 additions and 4 deletions
69
runtime/gc.c
69
runtime/gc.c
|
|
@ -238,10 +238,62 @@ bool is_valid_pointer(const size_t *p) {
|
||||||
return !UNBOXED(p);
|
return !UNBOXED(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void queue_enqueue(heap_iterator *tail_iter, void *obj) {
|
||||||
|
void *tail = tail_iter->current;
|
||||||
|
void *tail_content = get_object_content_ptr(tail);
|
||||||
|
set_forward_address(tail_content, (size_t) obj);
|
||||||
|
make_enqueued(obj);
|
||||||
|
heap_next_obj_iterator(tail_iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *queue_dequeue(heap_iterator *head_iter) {
|
||||||
|
void *head = head_iter->current;
|
||||||
|
void *head_content = get_object_content_ptr(head);
|
||||||
|
void *value = (void*) get_forward_address(head_content);
|
||||||
|
make_dequeued(value);
|
||||||
|
heap_next_obj_iterator(head_iter);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
void mark(void *obj) {
|
void mark(void *obj) {
|
||||||
if (!is_valid_heap_pointer(obj)) {
|
if (!is_valid_heap_pointer(obj)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_marked(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TL;DR: [q_head_iter, q_tail_iter) q_head_iter -- current dequeue's victim, q_tail_iter -- place for next enqueue
|
||||||
|
// in forward_address of corresponding element we store address of element to be removed after dequeue operation
|
||||||
|
heap_iterator q_head_iter = heap_begin_iterator();
|
||||||
|
// iterator where we will write address of the element that is going to be enqueued
|
||||||
|
heap_iterator q_tail_iter = q_head_iter;
|
||||||
|
queue_enqueue(&q_tail_iter, obj);
|
||||||
|
|
||||||
|
// invariant: queue contains only objects that are valid heap pointers (each corresponding to content of unmarked object)
|
||||||
|
// also each object is in queue only once
|
||||||
|
while (q_head_iter.current != q_tail_iter.current) { // means the queue is not empty
|
||||||
|
void *cur_obj = queue_dequeue(&q_head_iter);
|
||||||
|
mark_object(cur_obj);
|
||||||
|
void *header_ptr = get_obj_header_ptr(cur_obj, get_type_row_ptr(cur_obj));
|
||||||
|
for (
|
||||||
|
obj_field_iterator ptr_field_it = ptr_field_begin_iterator(header_ptr);
|
||||||
|
!field_is_done_iterator(&ptr_field_it);
|
||||||
|
obj_next_ptr_field_iterator(&ptr_field_it)
|
||||||
|
) {
|
||||||
|
void *field_value = * (void **) ptr_field_it.cur_field;
|
||||||
|
if (!is_valid_heap_pointer(field_value) || is_marked(field_value) || is_enqueued(field_value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if we came to this point it must be true that field_value is unmarked and not currently in queue
|
||||||
|
// thus, we maintain the invariant
|
||||||
|
queue_enqueue(&q_tail_iter, field_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* if (!is_valid_heap_pointer(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (is_marked(obj)) {
|
if (is_marked(obj)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +305,7 @@ void mark(void *obj) {
|
||||||
obj_next_ptr_field_iterator(&ptr_field_it)
|
obj_next_ptr_field_iterator(&ptr_field_it)
|
||||||
) {
|
) {
|
||||||
mark(* (void **) ptr_field_it.cur_field);
|
mark(* (void **) ptr_field_it.cur_field);
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void scan_extra_roots(void) {
|
void scan_extra_roots(void) {
|
||||||
|
|
@ -422,6 +474,21 @@ void unmark_object(void *obj) {
|
||||||
RESET_MARK_BIT(d->forward_address);
|
RESET_MARK_BIT(d->forward_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_enqueued(void *obj) {
|
||||||
|
data *d = TO_DATA(obj);
|
||||||
|
return IS_ENQUEUED(d->forward_address) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_enqueued(void *obj) {
|
||||||
|
data *d = TO_DATA(obj);
|
||||||
|
MAKE_ENQUEUED(d->forward_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_dequeued(void *obj) {
|
||||||
|
data *d = TO_DATA(obj);
|
||||||
|
MAKE_DEQUEUED(d->forward_address);
|
||||||
|
}
|
||||||
|
|
||||||
heap_iterator heap_begin_iterator() {
|
heap_iterator heap_begin_iterator() {
|
||||||
heap_iterator it = {.current=heap.begin};
|
heap_iterator it = {.current=heap.begin};
|
||||||
return it;
|
return it;
|
||||||
|
|
|
||||||
16
runtime/gc.h
16
runtime/gc.h
|
|
@ -5,9 +5,12 @@
|
||||||
|
|
||||||
# define GET_MARK_BIT(x) (((int) (x)) & 1)
|
# define GET_MARK_BIT(x) (((int) (x)) & 1)
|
||||||
# define SET_MARK_BIT(x) (x = (((int) (x)) | 1))
|
# define SET_MARK_BIT(x) (x = (((int) (x)) | 1))
|
||||||
|
# define IS_ENQUEUED(x) (((int) (x)) & 2)
|
||||||
|
# define MAKE_ENQUEUED(x) (x = (((int) (x)) | 2))
|
||||||
|
# define MAKE_DEQUEUED(x) (x = (((int) (x)) & (~2)))
|
||||||
# define RESET_MARK_BIT(x) (x = (((int) (x)) & (~1)))
|
# define RESET_MARK_BIT(x) (x = (((int) (x)) & (~1)))
|
||||||
# define GET_FORWARD_ADDRESS(x) (((size_t) (x)) & (~1)) // since last bit is used as mark-bit and due to correct alignment we can expect that last bit doesn'test_small_tree_compaction influence address (it should always be zero)
|
# define GET_FORWARD_ADDRESS(x) (((size_t) (x)) & (~3)) // since last 2 bits are used for mark-bit and enqueued-bit and due to correct alignment we can expect that last 2 bits don't influence address (they should always be zero)
|
||||||
# define SET_FORWARD_ADDRESS(x, addr) (x = (GET_MARK_BIT(x) | ((int) (addr))))
|
# define SET_FORWARD_ADDRESS(x, addr) (x = ((x & 3) | ((int) (addr)))) // take the last two bits as they are and make all others zero
|
||||||
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
# define MINIMUM_HEAP_CAPACITY (8)
|
# define MINIMUM_HEAP_CAPACITY (8)
|
||||||
|
|
@ -134,6 +137,15 @@ void mark_object(void *obj);
|
||||||
// takes a pointer to an object content as an argument, marks the object as dead
|
// takes a pointer to an object content as an argument, marks the object as dead
|
||||||
void unmark_object(void *obj);
|
void unmark_object(void *obj);
|
||||||
|
|
||||||
|
// takes a pointer to an object content as an argument, returns whether this object was enqueued to the queue (which is used in mark phase)
|
||||||
|
bool is_enqueued(void *obj);
|
||||||
|
|
||||||
|
// takes a pointer to an object content as an argument, marks object as enqueued
|
||||||
|
void make_enqueued(void *obj);
|
||||||
|
|
||||||
|
// takes a pointer to an object content as an argument, unmarks object as enqueued
|
||||||
|
void make_dequeued(void *obj);
|
||||||
|
|
||||||
// returns iterator to an object with the lowest address
|
// returns iterator to an object with the lowest address
|
||||||
heap_iterator heap_begin_iterator();
|
heap_iterator heap_begin_iterator();
|
||||||
void heap_next_obj_iterator(heap_iterator *it);
|
void heap_next_obj_iterator(heap_iterator *it);
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ size_t generate_random_obj_forest(virt_stack *st, int cnt, int seed) {
|
||||||
void run_stress_test_random_obj_forest(int seed) {
|
void run_stress_test_random_obj_forest(int seed) {
|
||||||
virt_stack *st = init_test();
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
const int SZ = 10000;
|
const int SZ = 100000;
|
||||||
|
|
||||||
size_t expectedAlive = generate_random_obj_forest(st, SZ, seed);
|
size_t expectedAlive = generate_random_obj_forest(st, SZ, seed);
|
||||||
|
|
||||||
|
|
@ -244,6 +244,8 @@ void run_stress_test_random_obj_forest(int seed) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
no_gc_tests();
|
no_gc_tests();
|
||||||
|
|
@ -257,9 +259,15 @@ int main(int argc, char ** argv) {
|
||||||
test_alive_are_not_reclaimed();
|
test_alive_are_not_reclaimed();
|
||||||
test_small_tree_compaction();
|
test_small_tree_compaction();
|
||||||
|
|
||||||
|
time_t start, end;
|
||||||
|
double diff;
|
||||||
|
time(&start);
|
||||||
// stress test
|
// stress test
|
||||||
for (int s = 0; s < 100; ++s) {
|
for (int s = 0; s < 100; ++s) {
|
||||||
run_stress_test_random_obj_forest(s);
|
run_stress_test_random_obj_forest(s);
|
||||||
}
|
}
|
||||||
|
time(&end);
|
||||||
|
diff = difftime(end, start);
|
||||||
|
printf ("Stress tests took %.2lf seconds to complete\n", diff);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue