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);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!is_valid_heap_pointer(obj)) {
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -253,7 +305,7 @@ void mark(void *obj) {
|
|||
obj_next_ptr_field_iterator(&ptr_field_it)
|
||||
) {
|
||||
mark(* (void **) ptr_field_it.cur_field);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void scan_extra_roots(void) {
|
||||
|
|
@ -422,6 +474,21 @@ void unmark_object(void *obj) {
|
|||
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 it = {.current=heap.begin};
|
||||
return it;
|
||||
|
|
|
|||
16
runtime/gc.h
16
runtime/gc.h
|
|
@ -5,9 +5,12 @@
|
|||
|
||||
# define GET_MARK_BIT(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 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 SET_FORWARD_ADDRESS(x, addr) (x = (GET_MARK_BIT(x) | ((int) (addr))))
|
||||
# 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 = ((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
|
||||
#ifdef DEBUG_VERSION
|
||||
# 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
|
||||
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
|
||||
heap_iterator heap_begin_iterator();
|
||||
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) {
|
||||
virt_stack *st = init_test();
|
||||
|
||||
const int SZ = 10000;
|
||||
const int SZ = 100000;
|
||||
|
||||
size_t expectedAlive = generate_random_obj_forest(st, SZ, seed);
|
||||
|
||||
|
|
@ -244,6 +244,8 @@ void run_stress_test_random_obj_forest(int seed) {
|
|||
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
#ifdef DEBUG_VERSION
|
||||
no_gc_tests();
|
||||
|
|
@ -257,9 +259,15 @@ int main(int argc, char ** argv) {
|
|||
test_alive_are_not_reclaimed();
|
||||
test_small_tree_compaction();
|
||||
|
||||
time_t start, end;
|
||||
double diff;
|
||||
time(&start);
|
||||
// stress test
|
||||
for (int s = 0; s < 100; ++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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue