Fixing up fixed-size heaps

This commit is contained in:
Justin Ethier 2018-07-16 18:33:30 -04:00
parent 7994354835
commit c89a434bf9

87
gc.c
View file

@ -528,8 +528,6 @@ size_t gc_convert_heap_page_to_free_list(gc_heap *h, gc_thread_data *thd)
* @brief Sweep portion of the GC algorithm * @brief Sweep portion of the GC algorithm
* @param h Heap to sweep * @param h Heap to sweep
* @param heap_type Type of heap, based on object sizes allocated on it * @param heap_type Type of heap, based on object sizes allocated on it
* @param sum_freed_ptr Out parameter tracking the sum of freed data, in bytes.
* This parameter is ignored if NULL is passed.
* @param thd Thread data object for the mutator using this heap * @param thd Thread data object for the mutator using this heap
* @return Return the size of the largest object freed, in bytes * @return Return the size of the largest object freed, in bytes
* *
@ -537,17 +535,17 @@ size_t gc_convert_heap_page_to_free_list(gc_heap *h, gc_thread_data *thd)
* memory slots to the heap. It is only called by the collector thread after * memory slots to the heap. It is only called by the collector thread after
* the heap has been traced to identify live objects. * the heap has been traced to identify live objects.
*/ */
void gc_sweep_fixed_size(gc_heap * h, int heap_type, size_t * sum_freed_ptr, gc_thread_data *thd) gc_heap *gc_sweep_fixed_size(gc_heap * h, int heap_type, gc_thread_data *thd)
{ {
// TODO: all of this needs to be reworked, see gc_sweep // TODO: all of this needs to be reworked, see gc_sweep
size_t heap_freed = 0, sum_freed = 0; //size_t heap_freed = 0, sum_freed = 0;
short heap_is_empty; short heap_is_empty;
object p; object p;
gc_free_list *q, *r, *s; gc_free_list *q, *r, *s;
#if GC_DEBUG_SHOW_SWEEP_DIAG #if GC_DEBUG_SHOW_SWEEP_DIAG
gc_heap *orig_heap_ptr = h; gc_heap *orig_heap_ptr = h;
#endif #endif
gc_heap *prev_h = NULL; //gc_heap *prev_h = NULL;
gc_heap *rv = h; gc_heap *rv = h;
h->next_free = h; h->next_free = h;
@ -561,7 +559,7 @@ void gc_sweep_fixed_size(gc_heap * h, int heap_type, size_t * sum_freed_ptr, gc_
if (h->data_end != NULL) { if (h->data_end != NULL) {
// Special case, bump&pop heap // Special case, bump&pop heap
heap_freed = gc_convert_heap_page_to_free_list(h, thd); gc_convert_heap_page_to_free_list(h, thd);
heap_is_empty = 0; // For now, don't try to free bump&pop heap_is_empty = 0; // For now, don't try to free bump&pop
} else { } else {
//gc_free_list *next; //gc_free_list *next;
@ -629,7 +627,7 @@ void gc_sweep_fixed_size(gc_heap * h, int heap_type, size_t * sum_freed_ptr, gc_
} }
// free p // free p
heap_freed += h->block_size; //heap_freed += h->block_size;
if (h->free_list == NULL) { if (h->free_list == NULL) {
// No free list, start one at p // No free list, start one at p
q = h->free_list = p; q = h->free_list = p;
@ -650,7 +648,7 @@ void gc_sweep_fixed_size(gc_heap * h, int heap_type, size_t * sum_freed_ptr, gc_
q->next = s; q->next = s;
//printf("sweep reclaimed remaining=%d, %p, q=%p, r=%p\n", remaining, p, q, r); //printf("sweep reclaimed remaining=%d, %p, q=%p, r=%p\n", remaining, p, q, r);
} }
h->free_size += h->block_size;
} else { } else {
//printf("sweep block is still used remaining=%d p = %p\n", remaining, p); //printf("sweep block is still used remaining=%d p = %p\n", remaining, p);
} }
@ -749,7 +747,13 @@ gc_heap *gc_heap_free(gc_heap *page, gc_heap *prev_page)
int gc_is_heap_empty(gc_heap *h) int gc_is_heap_empty(gc_heap *h)
{ {
gc_free_list *f; gc_free_list *f;
if (!h || !h->free_list) return 0; if (!h) return 0;
if (h->data_end) { // Bump&pop
return (h->remaining == (h->size - (h->size % h->block_size)));
}
if (!h->free_list) return 0;
f = h->free_list; f = h->free_list;
if (f->size != 0 || !f->next) return 0; if (f->size != 0 || !f->next) return 0;
@ -1232,12 +1236,63 @@ void *gc_try_alloc_fixed_size(gc_heap * h, int heap_type, size_t size, char *obj
ck_pr_sub_ptr(&(thd->cached_heap_free_sizes[heap_type]), size); ck_pr_sub_ptr(&(thd->cached_heap_free_sizes[heap_type]), size);
h_passed->next_free = h; h_passed->next_free = h;
h->free_size -= size;
return result; return result;
} }
} }
return NULL; return NULL;
} }
void *gc_try_alloc_slow_fixed_size(gc_heap *h_passed, gc_heap *h, int heap_type, size_t size, char *obj, gc_thread_data *thd)
{
gc_heap *h_start = h, *h_prev;
void *result = NULL;
// Find next heap
while (result == NULL) {
h_prev = h;
h = h->next;
if (h == NULL) {
// Wrap around to the first heap block
h_prev = NULL;
h = h_passed;
}
if (h == h_start) {
// Tried all and no heap exists with free space
break;
}
// check allocation status to make sure we can use it
if (h->is_full) {
continue; // Cannot sweep until next GC cycle
} else if (h->cached_free_size_status == 1 && !gc_is_heap_empty(h)) {
unsigned int h_size = h->size;
gc_heap *keep = gc_sweep_fixed_size(h, heap_type, thd); // Clean up garbage objects
if (!keep) {
// Heap marked for deletion, remove it and keep searching
gc_heap *freed = gc_heap_free(h, h_prev);
if (freed) {
if (h_prev) {
h = h_prev;
} else {
h = h_passed;
}
//thd->cached_heap_free_sizes[heap_type] -= prev_free_size;
thd->cached_heap_total_sizes[heap_type] -= h_size;
continue;
}
}
}
result = gc_try_alloc_fixed_size(h, heap_type, size, obj, thd);
if (result) {
h_passed->next_free = h;
h_passed->last_alloc_size = size;
} else {
// TODO: else, assign heap full? YES for fixed-size, for REST maybe not??
h->is_full = 1;
}
}
return result;
}
/** /**
* @brief A convenience function for allocating bignums * @brief A convenience function for allocating bignums
* @param data The mutator's thread data object * @param data The mutator's thread data object
@ -1294,6 +1349,7 @@ void *gc_alloc(gc_heap_root * hrt, size_t size, char *obj, gc_thread_data * thd,
gc_heap *h_passed, *h = NULL; gc_heap *h_passed, *h = NULL;
int heap_type; int heap_type;
void *(*try_alloc)(gc_heap * h, int heap_type, size_t size, char *obj, gc_thread_data * thd); void *(*try_alloc)(gc_heap * h, int heap_type, size_t size, char *obj, gc_thread_data * thd);
void *(*try_alloc_slow)(gc_heap *h_passed, gc_heap *h, int heap_type, size_t size, char *obj, gc_thread_data *thd);
// TODO: check return value, if null (could not alloc) then // TODO: check return value, if null (could not alloc) then
// run a collection and check how much free space there is. if less // run a collection and check how much free space there is. if less
// the allowed ratio, try growing heap. // the allowed ratio, try growing heap.
@ -1302,26 +1358,29 @@ void *gc_alloc(gc_heap_root * hrt, size_t size, char *obj, gc_thread_data * thd,
if (size <= 32) { if (size <= 32) {
heap_type = HEAP_SM; heap_type = HEAP_SM;
try_alloc = &gc_try_alloc_fixed_size; try_alloc = &gc_try_alloc_fixed_size;
try_alloc_slow = &gc_try_alloc_slow_fixed_size;
} else if (size <= 64) { } else if (size <= 64) {
heap_type = HEAP_64; heap_type = HEAP_64;
try_alloc = &gc_try_alloc_fixed_size; try_alloc = &gc_try_alloc_fixed_size;
try_alloc_slow = &gc_try_alloc_slow_fixed_size;
// Only use this heap on 64-bit platforms, where larger objs are used more often // Only use this heap on 64-bit platforms, where larger objs are used more often
// Code from http://stackoverflow.com/a/32717129/101258 // Code from http://stackoverflow.com/a/32717129/101258
#if INTPTR_MAX == INT64_MAX #if INTPTR_MAX == INT64_MAX
} else if (size <= 96) { } else if (size <= 96) {
heap_type = HEAP_96; heap_type = HEAP_96;
try_alloc = &gc_try_alloc_fixed_size; try_alloc = &gc_try_alloc_fixed_size;
try_alloc_slow = &gc_try_alloc_slow_fixed_size;
#endif #endif
} else if (size >= MAX_STACK_OBJ) { } else if (size >= MAX_STACK_OBJ) {
heap_type = HEAP_HUGE; heap_type = HEAP_HUGE;
try_alloc = &gc_try_alloc; try_alloc = &gc_try_alloc;
try_alloc_slow = &gc_try_alloc_slow;
} else { } else {
heap_type = HEAP_REST; heap_type = HEAP_REST;
try_alloc = &gc_try_alloc; try_alloc = &gc_try_alloc;
try_alloc_slow = &gc_try_alloc_slow;
} }
TODO: in addition to below, probably need to update fixed size code to work with gc_heap_free_size and gc_is_heap_empty (though for that one we may just call another function in the fixed-size heap code //TODO: convert fixed-size heap code and use that here. BUT, create a version of gc_alloc (maybe using macros?)
TODO: convert fixed-size heap code and use that here. BUT, create a version of gc_alloc (maybe using macros?)
//that accepts heap type as an arg and can assume free lists. we can modify gc_move to use the proper new //that accepts heap type as an arg and can assume free lists. we can modify gc_move to use the proper new
//version of gc_alloc (just ifdef if need be for 32 vs 64 bit. this might speed things up a bit //version of gc_alloc (just ifdef if need be for 32 vs 64 bit. this might speed things up a bit
h = hrt->heap[heap_type]; h = hrt->heap[heap_type];
@ -1341,7 +1400,7 @@ TODO: convert fixed-size heap code and use that here. BUT, create a version of g
} else { } else {
// Slow path, find another heap block // Slow path, find another heap block
h->is_full = 1; h->is_full = 1;
result = gc_try_alloc_slow(h_passed, h, heap_type, size, obj, thd); result = try_alloc_slow(h_passed, h, heap_type, size, obj, thd);
#if GC_DEBUG_VERBOSE #if GC_DEBUG_VERBOSE
fprintf(stderr, "slow alloc of %p\n", result); fprintf(stderr, "slow alloc of %p\n", result);
#endif #endif
@ -1361,7 +1420,7 @@ fprintf(stderr, "slow alloc of %p\n", result);
// TODO: would be nice if gc_grow_heap returns new page (maybe it does) then we can start from there // TODO: would be nice if gc_grow_heap returns new page (maybe it does) then we can start from there
// otherwise will be a bit of a bottleneck since with lazy sweeping there is no guarantee we are at // otherwise will be a bit of a bottleneck since with lazy sweeping there is no guarantee we are at
// the end of the heap anymore // the end of the heap anymore
result = gc_try_alloc_slow(h_passed, h, heap_type, size, obj, thd); result = try_alloc_slow(h_passed, h, heap_type, size, obj, thd);
#if GC_DEBUG_VERBOSE #if GC_DEBUG_VERBOSE
fprintf(stderr, "slowest alloc of %p\n", result); fprintf(stderr, "slowest alloc of %p\n", result);
#endif #endif