ruby-changes:71589
From: Matt <ko1@a...>
Date: Fri, 1 Apr 2022 21:46:13 +0900 (JST)
Subject: [ruby-changes:71589] 76572e5a7f (master): [Feature #18619] Reverse the order of compaction movement
https://git.ruby-lang.org/ruby.git/commit/?id=76572e5a7f From 76572e5a7fc0ffde6501fd9a8c034bb621f11688 Mon Sep 17 00:00:00 2001 From: Matt Valentine-House <matt@e...> Date: Thu, 6 Jan 2022 22:29:03 +0000 Subject: [Feature #18619] Reverse the order of compaction movement This commit changes the way compaction moves objects and sweeps pages in order to better facilitate object movement between size pools. Previously we would move the scan cursor first until we found an empty slot and then we'd decrement the compact cursor until we found something to move into that slot. We would sweep the page that contained the scan cursor before trying to fill it In this algorithm we first move the compact cursor down until we find an object to move - We then take a free page from the desired destination heap (always the same heap in this current iteration of the code). If there is no free page we sweep the page at the sweeping_page cursor, add it to the free pages, and advance the cursor to the next page, and try again. We sweep one page from each size pool in this way, and then repeat that process until all the size pools are compacted (all the cursors have met), and then we update references and sweep the rest of the heap. --- gc.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 233 insertions(+), 60 deletions(-) diff --git a/gc.c b/gc.c index d620b66676..cf0a70306a 100644 --- a/gc.c +++ b/gc.c @@ -709,7 +709,8 @@ typedef struct rb_size_pool_struct { https://github.com/ruby/ruby/blob/trunk/gc.c#L709 enum gc_mode { gc_mode_none, gc_mode_marking, - gc_mode_sweeping + gc_mode_sweeping, + gc_mode_compacting, }; typedef struct rb_objspace { @@ -1007,6 +1008,7 @@ gc_mode_verify(enum gc_mode mode) https://github.com/ruby/ruby/blob/trunk/gc.c#L1008 case gc_mode_none: case gc_mode_marking: case gc_mode_sweeping: + case gc_mode_compacting: break; default: rb_bug("gc_mode_verify: unreachable (%d)", (int)mode); @@ -5040,6 +5042,44 @@ try_move_plane(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page, https://github.com/ruby/ruby/blob/trunk/gc.c#L5042 return false; } +static bool +try_move2(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *free_page, VALUE src) +{ + if (!free_page) { + return false; + } + + /* We should return true if either src is sucessfully moved, or src is + * unmoveable. A false return will cause the sweeping cursor to be + * incremented to the next page, and src will attempt to move again */ + if (gc_is_moveable_obj(objspace, src)) { + GC_ASSERT(MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(src), src)); + + VALUE dest = (VALUE)free_page->freelist; + asan_unpoison_object(dest, false); + if (!dest) { + /* if we can't get something from the freelist then the page must be + * full */ + return false; + } + free_page->freelist = RANY(dest)->as.free.next; + + GC_ASSERT(RB_BUILTIN_TYPE(dest) == T_NONE); + GC_ASSERT(free_page->slot_size == GET_HEAP_PAGE(src)->slot_size); + + objspace->rcompactor.moved_count_table[BUILTIN_TYPE(src)]++; + objspace->rcompactor.total_moved++; + + //fprintf(stderr, "\t\tMoving objects: %s\n\t\t\t-> %s\n", obj_info(src), obj_info(dest)); + gc_move(objspace, src, dest, free_page->slot_size); + gc_pin(objspace, src); + FL_SET(src, FL_FROM_FREELIST); + free_page->free_slots--; + } + + return true; +} + static short try_move(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_page, VALUE dest) { @@ -5289,8 +5329,10 @@ check_stack_for_moved(rb_objspace_t *objspace) https://github.com/ruby/ruby/blob/trunk/gc.c#L5329 each_machine_stack_value(ec, revert_machine_stack_references); } +static void gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode); + static void -gc_compact_finish(rb_objspace_t *objspace, rb_size_pool_t *pool, rb_heap_t *heap) +gc_compact_finish(rb_objspace_t *objspace) { for (int i = 0; i < SIZE_POOL_COUNT; i++) { rb_size_pool_t *size_pool = &size_pools[i]; @@ -5314,6 +5356,7 @@ gc_compact_finish(rb_objspace_t *objspace, rb_size_pool_t *pool, rb_heap_t *heap https://github.com/ruby/ruby/blob/trunk/gc.c#L5356 rb_size_pool_t *size_pool = &size_pools[i]; rb_heap_t *heap = SIZE_POOL_EDEN_HEAP(size_pool); heap->compact_cursor = NULL; + heap->free_pages = NULL; heap->compact_cursor_index = 0; } @@ -5448,16 +5491,12 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit https://github.com/ruby/ruby/blob/trunk/gc.c#L5491 } #endif if (obj_free(objspace, vp)) { - if (heap->compact_cursor) { - /* We *want* to fill this slot */ - MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(vp), vp); - } - else { - (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE); - heap_page_add_freeobj(objspace, sweep_page, vp); - gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp)); - ctx->freed_slots++; - } + // always add free slots back to the swept pages freelist, + // so that if we're comapacting, we can re-use the slots + (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE); + heap_page_add_freeobj(objspace, sweep_page, vp); + gc_report(3, objspace, "page_sweep: %s is added to freelist\n", obj_info(vp)); + ctx->freed_slots++; } else { ctx->final_slots++; @@ -5486,13 +5525,7 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit https://github.com/ruby/ruby/blob/trunk/gc.c#L5525 /* already counted */ break; case T_NONE: - if (heap->compact_cursor) { - /* We *want* to fill this slot */ - MARK_IN_BITMAP(GET_HEAP_PINNED_BITS(vp), vp); - } - else { - ctx->empty_slots++; /* already freed */ - } + ctx->empty_slots++; /* already freed */ break; } } @@ -5502,7 +5535,7 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit https://github.com/ruby/ruby/blob/trunk/gc.c#L5535 } static inline void -gc_sweep_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap, struct gc_sweep_context *ctx) +gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context *ctx) { struct heap_page *sweep_page = ctx->page; @@ -5513,27 +5546,18 @@ gc_sweep_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *hea https://github.com/ruby/ruby/blob/trunk/gc.c#L5546 gc_report(2, objspace, "page_sweep: start.\n"); - if (heap->compact_cursor) { - if (sweep_page == heap->compact_cursor) { - /* The compaction cursor and sweep page met, so we need to quit compacting */ - gc_report(5, objspace, "Quit compacting, mark and compact cursor met\n"); - gc_compact_finish(objspace, size_pool, heap); - } - else { - /* We anticipate filling the page, so NULL out the freelist. */ - asan_unpoison_memory_region(&sweep_page->freelist, sizeof(RVALUE*), false); - sweep_page->freelist = NULL; - asan_poison_memory_region(&sweep_page->freelist, sizeof(RVALUE*)); - } +#if RGENGC_CHECK_MODE + if (!objspace->flags.immediate_sweep) { + GC_ASSERT(sweep_page->flags.before_sweep == TRUE); } - +#endif sweep_page->flags.before_sweep = FALSE; sweep_page->free_slots = 0; p = (uintptr_t)sweep_page->start; bits = sweep_page->mark_bits; - int page_rvalue_count = sweep_page->total_slots * (size_pool->slot_size / BASE_SLOT_SIZE); + int page_rvalue_count = sweep_page->total_slots * (sweep_page->slot_size / BASE_SLOT_SIZE); int out_of_range_bits = (NUM_IN_PAGE(p) + page_rvalue_count) % BITS_BITLENGTH; if (out_of_range_bits != 0) { // sizeof(RVALUE) == 64 bits[BITMAP_INDEX(p) + page_rvalue_count / BITS_BITLENGTH] |= ~(((bits_t)1 << out_of_range_bits) - 1); @@ -5555,12 +5579,6 @@ gc_sweep_page(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *hea https://github.com/ruby/ruby/blob/trunk/gc.c#L5579 p += BITS_BITLENGTH * BASE_SLOT_SIZE; } - if (heap->compact_cursor) { - if (gc_fill_swept_page(objspace, heap, sweep_page, ctx)) { - gc_compact_finish(objspace, size_pool, heap); - } - } - if (!heap->compact_cursor) { gc_setup_mark_bits(sweep_page); } @@ -5626,6 +5644,7 @@ gc_mode_name(enum gc_mode mode) https://github.com/ruby/ruby/blob/trunk/gc.c#L5644 case gc_mode_none: return "none"; case gc_mode_marking: return "marking"; case gc_mode_sweeping: return "sweeping"; + case gc_mode_compacting: return "compacting"; default: rb_bug("gc_mode_name: unknown mode: %d", (int)mode); } } @@ -5638,7 +5657,8 @@ gc_mode_transition(rb_objspace_t *objspace, enum gc_mode mode) https://github.com/ruby/ruby/blob/trunk/gc.c#L5657 switch (prev_mode) { case gc_mode_none: GC_ASSERT(mode == gc_mode_marking); break; case gc_mode_marking: GC_ASSERT(mode == gc_mode_sweeping); break; - case gc_mode_sweeping: GC_ASSERT(mode == gc_mode_none); break; + case gc_mode_sweeping: GC_ASSERT(mode == gc_mode_none || mode == gc_mode_c (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/