implemented non-recursive mark phase, added time measurement for stress tests

This commit is contained in:
Egor Sheremetov 2023-05-31 01:06:53 +02:00
parent 5da89d0c2e
commit 9107a26668
3 changed files with 91 additions and 4 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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
} }