Skip to content

Commit 682cf1f

Browse files
FletcherDaresclaude
andcommitted
gc: skip unmarked slots in gc_sweep_plane via ntz
Iterate gc_sweep_plane by jumping directly to each set bit using ntz_intptr and clearing it with `bitset &= bitset - 1`, instead of walking every slot in the plane and testing `bitset & 1`. Callers mask the bitset with heap->slot_bits_mask, so the lowest set bit always lands on a slot boundary; slot addresses are computed as base + bit * BASE_SLOT_SIZE. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6ae5083 commit 682cf1f

1 file changed

Lines changed: 58 additions & 53 deletions

File tree

gc/default/default.c

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
#ifdef BUILDING_MODULAR_GC
1919
# define nlz_int64(x) (x == 0 ? 64 : (unsigned int)__builtin_clzll((unsigned long long)x))
20+
# define ntz_intptr(x) \
21+
((x) == 0 ? (int)(sizeof(uintptr_t) * CHAR_BIT) : \
22+
(int)__builtin_ctzll((unsigned long long)(x)))
2023
#else
2124
# include "internal/bits.h"
2225
#endif
@@ -3470,81 +3473,83 @@ struct gc_sweep_context {
34703473
};
34713474

34723475
static inline void
3473-
gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bitset, struct gc_sweep_context *ctx)
3476+
gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t base, bits_t bitset, struct gc_sweep_context *ctx)
34743477
{
34753478
struct heap_page *sweep_page = ctx->page;
3476-
short slot_size = sweep_page->slot_size;
3477-
short slot_bits = slot_size / BASE_SLOT_SIZE;
3478-
GC_ASSERT(slot_bits > 0);
34793479

3480+
/* Callers guarantee bitset != 0 on entry, and mask it with
3481+
* heap->slot_bits_mask so only the aligned starting bit of each slot is
3482+
* set. The lowest set bit therefore always lands on a slot boundary, and
3483+
* we can jump straight from one marked slot to the next instead of
3484+
* walking every slot in the plane. */
34803485
do {
3486+
int bit = ntz_intptr(bitset);
3487+
uintptr_t p = base + (uintptr_t)bit * BASE_SLOT_SIZE;
34813488
VALUE vp = (VALUE)p;
34823489
GC_ASSERT(vp % BASE_SLOT_SIZE == 0);
3490+
GC_ASSERT(((uintptr_t)bit * BASE_SLOT_SIZE) % sweep_page->slot_size == 0);
34833491

34843492
rb_asan_unpoison_object(vp, false);
3485-
if (bitset & 1) {
3486-
switch (BUILTIN_TYPE(vp)) {
3487-
default: /* majority case */
3488-
gc_report(2, objspace, "page_sweep: free %p\n", (void *)p);
3493+
switch (BUILTIN_TYPE(vp)) {
3494+
default: /* majority case */
3495+
gc_report(2, objspace, "page_sweep: free %p\n", (void *)p);
34893496
#if RGENGC_CHECK_MODE
3490-
if (!is_full_marking(objspace)) {
3491-
if (RVALUE_OLD_P(objspace, vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p);
3492-
if (RVALUE_REMEMBERED(objspace, vp)) rb_bug("page_sweep: %p - remembered.", (void *)p);
3493-
}
3497+
if (!is_full_marking(objspace)) {
3498+
if (RVALUE_OLD_P(objspace, vp)) rb_bug("page_sweep: %p - old while minor GC.", (void *)p);
3499+
if (RVALUE_REMEMBERED(objspace, vp)) rb_bug("page_sweep: %p - remembered.", (void *)p);
3500+
}
34943501
#endif
34953502

3496-
if (RVALUE_WB_UNPROTECTED(objspace, vp)) CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(vp), vp);
3503+
if (RVALUE_WB_UNPROTECTED(objspace, vp)) CLEAR_IN_BITMAP(GET_HEAP_WB_UNPROTECTED_BITS(vp), vp);
34973504

34983505
#if RGENGC_CHECK_MODE
34993506
#define CHECK(x) if (x(objspace, vp) != FALSE) rb_bug("obj_free: " #x "(%s) != FALSE", rb_obj_info(vp))
3500-
CHECK(RVALUE_WB_UNPROTECTED);
3501-
CHECK(RVALUE_MARKED);
3502-
CHECK(RVALUE_MARKING);
3503-
CHECK(RVALUE_UNCOLLECTIBLE);
3507+
CHECK(RVALUE_WB_UNPROTECTED);
3508+
CHECK(RVALUE_MARKED);
3509+
CHECK(RVALUE_MARKING);
3510+
CHECK(RVALUE_UNCOLLECTIBLE);
35043511
#undef CHECK
35053512
#endif
35063513

3507-
rb_gc_event_hook(vp, RUBY_INTERNAL_EVENT_FREEOBJ);
3514+
rb_gc_event_hook(vp, RUBY_INTERNAL_EVENT_FREEOBJ);
35083515

3509-
rb_gc_obj_free_vm_weak_references(vp);
3510-
if (rb_gc_obj_free(objspace, vp)) {
3511-
// always add free slots back to the swept pages freelist,
3512-
// so that if we're compacting, we can re-use the slots
3513-
(void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE);
3514-
RVALUE_AGE_SET_BITMAP(vp, 0);
3515-
heap_page_add_freeobj(objspace, sweep_page, vp);
3516-
gc_report(3, objspace, "page_sweep: %s is added to freelist\n", rb_obj_info(vp));
3517-
ctx->freed_slots++;
3518-
}
3519-
else {
3520-
ctx->final_slots++;
3521-
}
3522-
break;
3523-
3524-
case T_MOVED:
3525-
if (objspace->flags.during_compacting) {
3526-
/* The sweep cursor shouldn't have made it to any
3527-
* T_MOVED slots while the compact flag is enabled.
3528-
* The sweep cursor and compact cursor move in
3529-
* opposite directions, and when they meet references will
3530-
* get updated and "during_compacting" should get disabled */
3531-
rb_bug("T_MOVED shouldn't be seen until compaction is finished");
3532-
}
3533-
gc_report(3, objspace, "page_sweep: %s is added to freelist\n", rb_obj_info(vp));
3534-
ctx->empty_slots++;
3516+
rb_gc_obj_free_vm_weak_references(vp);
3517+
if (rb_gc_obj_free(objspace, vp)) {
3518+
// always add free slots back to the swept pages freelist,
3519+
// so that if we're compacting, we can re-use the slots
3520+
(void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, BASE_SLOT_SIZE);
35353521
RVALUE_AGE_SET_BITMAP(vp, 0);
35363522
heap_page_add_freeobj(objspace, sweep_page, vp);
3537-
break;
3538-
case T_ZOMBIE:
3539-
/* already counted */
3540-
break;
3541-
case T_NONE:
3542-
ctx->empty_slots++; /* already freed */
3543-
break;
3523+
gc_report(3, objspace, "page_sweep: %s is added to freelist\n", rb_obj_info(vp));
3524+
ctx->freed_slots++;
3525+
}
3526+
else {
3527+
ctx->final_slots++;
35443528
}
3529+
break;
3530+
3531+
case T_MOVED:
3532+
if (objspace->flags.during_compacting) {
3533+
/* The sweep cursor shouldn't have made it to any
3534+
* T_MOVED slots while the compact flag is enabled.
3535+
* The sweep cursor and compact cursor move in
3536+
* opposite directions, and when they meet references will
3537+
* get updated and "during_compacting" should get disabled */
3538+
rb_bug("T_MOVED shouldn't be seen until compaction is finished");
3539+
}
3540+
gc_report(3, objspace, "page_sweep: %s is added to freelist\n", rb_obj_info(vp));
3541+
ctx->empty_slots++;
3542+
RVALUE_AGE_SET_BITMAP(vp, 0);
3543+
heap_page_add_freeobj(objspace, sweep_page, vp);
3544+
break;
3545+
case T_ZOMBIE:
3546+
/* already counted */
3547+
break;
3548+
case T_NONE:
3549+
ctx->empty_slots++; /* already freed */
3550+
break;
35453551
}
3546-
p += slot_size;
3547-
bitset >>= slot_bits;
3552+
bitset &= bitset - 1;
35483553
} while (bitset);
35493554
}
35503555

0 commit comments

Comments
 (0)