diff options
author | Peter Zhu <peter@peterzhu.ca> | 2023-02-14 15:55:25 -0500 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2023-02-21 08:05:31 -0500 |
commit | 93ac7405b80cc61930d73da04441fa09af1851e1 (patch) | |
tree | 03b5a1f70186beee0b1dd01a44dfeda47f11d237 /gc.c | |
parent | d7c1ca48bf7754a23bfe1559422736029b4787a0 (diff) | |
download | ruby-93ac7405b80cc61930d73da04441fa09af1851e1.tar.gz |
Add marking and sweeping time to GC.stat
There is a `time` key in GC.stat that gives us the total time spent in
GC. However, we don't know what proportion of the time is spent between
marking and sweeping. This makes it difficult to tune the GC as we're
not sure where to focus our efforts on.
This PR adds keys `marking_time` and `sweeping_time` to GC.stat for the
time spent marking and sweeping, in milliseconds.
[Feature #19437]
Diffstat (limited to 'gc.c')
-rw-r--r-- | gc.c | 140 |
1 files changed, 91 insertions, 49 deletions
@@ -826,8 +826,10 @@ typedef struct rb_objspace { /* basic statistics */ size_t count; size_t total_freed_objects; - uint64_t total_time_ns; - struct timespec start_time; + uint64_t marking_time_ns; + struct timespec marking_start_time; + uint64_t sweeping_time_ns; + struct timespec sweeping_start_time; } profile; struct gc_list *global_list; @@ -1242,6 +1244,10 @@ enum gc_enter_event { static inline void gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev); static inline void gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_lev); +static void gc_marking_enter(rb_objspace_t *objspace); +static void gc_marking_exit(rb_objspace_t *objspace); +static void gc_sweeping_enter(rb_objspace_t *objspace); +static void gc_sweeping_exit(rb_objspace_t *objspace); static void gc_marks_start(rb_objspace_t *objspace, int full); static void gc_marks_finish(rb_objspace_t *objspace); @@ -4572,7 +4578,7 @@ rb_objspace_call_finalizer(rb_objspace_t *objspace) } } - gc_exit(objspace, false, &lock_lev); + gc_exit(objspace, gc_enter_event_finalizer, &lock_lev); finalize_deferred_heap_pages(objspace); @@ -6058,6 +6064,8 @@ gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *sweep_size_pool, rb_h GC_ASSERT(dont_gc_val() == FALSE); if (!GC_ENABLE_LAZY_SWEEP) return; + gc_sweeping_enter(objspace); + for (int i = 0; i < SIZE_POOL_COUNT; i++) { rb_size_pool_t *size_pool = &size_pools[i]; if (!gc_sweep_step(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool))) { @@ -6076,6 +6084,8 @@ gc_sweep_continue(rb_objspace_t *objspace, rb_size_pool_t *sweep_size_pool, rb_h #endif } } + + gc_sweeping_exit(objspace); } static void @@ -6186,6 +6196,8 @@ static void gc_sweep_compact(rb_objspace_t *objspace); static void gc_sweep(rb_objspace_t *objspace) { + gc_sweeping_enter(objspace); + const unsigned int immediate_sweep = objspace->flags.immediate_sweep; gc_report(1, objspace, "gc_sweep: immediate: %d\n", immediate_sweep); @@ -6217,6 +6229,8 @@ gc_sweep(rb_objspace_t *objspace) rb_size_pool_t *size_pool = &size_pools[0]; gc_heap_prepare_minimum_pages(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool)); #endif + + gc_sweeping_exit(objspace); } /* Marking - Marking stack */ @@ -8709,6 +8723,8 @@ gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t GC_ASSERT(dont_gc_val() == FALSE); bool marking_finished = true; + gc_marking_enter(objspace); + #if GC_ENABLE_INCREMENTAL_MARK if (heap->free_pages) { gc_report(2, objspace, "gc_marks_continue: has pooled pages"); @@ -8722,6 +8738,8 @@ gc_marks_continue(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t } #endif + gc_marking_exit(objspace); + return marking_finished; } @@ -8729,6 +8747,8 @@ static bool gc_marks(rb_objspace_t *objspace, int full_mark) { gc_prof_mark_timer_start(objspace); + gc_marking_enter(objspace); + bool marking_finished = false; /* setup marking */ @@ -8745,6 +8765,8 @@ gc_marks(rb_objspace_t *objspace, int full_mark) record->old_objects = objspace->rgengc.old_objects; } #endif + + gc_marking_exit(objspace); gc_prof_mark_timer_stop(objspace); return marking_finished; @@ -9549,12 +9571,19 @@ gc_rest(rb_objspace_t *objspace) if (RGENGC_CHECK_MODE >= 2) gc_verify_internal_consistency(objspace); if (is_incremental_marking(objspace)) { + gc_marking_enter(objspace); gc_marks_rest(objspace); + gc_marking_exit(objspace); + gc_sweep(objspace); } + if (is_lazy_sweeping(objspace)) { + gc_sweeping_enter(objspace); gc_sweep_rest(objspace); + gc_sweeping_exit(objspace); } + gc_exit(objspace, gc_enter_event_rest, &lock_lev); } } @@ -9669,58 +9698,30 @@ gc_enter_count(enum gc_enter_event event) #define MEASURE_GC (objspace->flags.measure_gc) #endif -static bool -gc_enter_event_measure_p(rb_objspace_t *objspace, enum gc_enter_event event) -{ - if (!MEASURE_GC) return false; - - switch (event) { - case gc_enter_event_start: - case gc_enter_event_continue: - case gc_enter_event_rest: - return true; - - default: - // case gc_enter_event_finalizer: - // case gc_enter_event_rb_memerror: - return false; - } -} - static bool current_process_time(struct timespec *ts); static void -gc_enter_clock(rb_objspace_t *objspace, enum gc_enter_event event) +gc_clock_start(struct timespec *ts) { - if (gc_enter_event_measure_p(objspace, event)) { - if (!current_process_time(&objspace->profile.start_time)) { - objspace->profile.start_time.tv_sec = 0; - objspace->profile.start_time.tv_nsec = 0; - } + if (!current_process_time(ts)) { + ts->tv_sec = 0; + ts->tv_nsec = 0; } } -static void -gc_exit_clock(rb_objspace_t *objspace, enum gc_enter_event event) +static uint64_t +gc_clock_end(struct timespec *ts) { - if (gc_enter_event_measure_p(objspace, event)) { - struct timespec end_time; + struct timespec end_time; - if ((objspace->profile.start_time.tv_sec > 0 || - objspace->profile.start_time.tv_nsec > 0) && - current_process_time(&end_time)) { - - if (end_time.tv_sec < objspace->profile.start_time.tv_sec) { - return; // ignore - } - else { - uint64_t ns = - (uint64_t)(end_time.tv_sec - objspace->profile.start_time.tv_sec) * (1000 * 1000 * 1000) + - (end_time.tv_nsec - objspace->profile.start_time.tv_nsec); - objspace->profile.total_time_ns += ns; - } - } + if ((ts->tv_sec > 0 || ts->tv_nsec > 0) && + current_process_time(&end_time) && + end_time.tv_sec >= ts->tv_sec) { + return (uint64_t)(end_time.tv_sec - ts->tv_sec) * (1000 * 1000 * 1000) + + (end_time.tv_nsec - ts->tv_nsec); } + + return 0; } static inline void @@ -9728,8 +9729,6 @@ gc_enter(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_ { RB_VM_LOCK_ENTER_LEV(lock_lev); - gc_enter_clock(objspace, event); - switch (event) { case gc_enter_event_rest: if (!is_marking(objspace)) break; @@ -9765,7 +9764,6 @@ gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_l gc_report(1, objspace, "gc_exit: %s [%s]\n", gc_enter_event_cstr(event), gc_current_status(objspace)); during_gc = FALSE; - gc_exit_clock(objspace, event); RB_VM_LOCK_LEAVE_LEV(lock_lev); #if RGENGC_CHECK_MODE >= 2 @@ -9777,6 +9775,38 @@ gc_exit(rb_objspace_t *objspace, enum gc_enter_event event, unsigned int *lock_l #endif } +static void +gc_marking_enter(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + gc_clock_start(&objspace->profile.marking_start_time); +} + +static void +gc_marking_exit(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + objspace->profile.marking_time_ns += gc_clock_end(&objspace->profile.marking_start_time); +} + +static void +gc_sweeping_enter(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + gc_clock_start(&objspace->profile.sweeping_start_time); +} + +static void +gc_sweeping_exit(rb_objspace_t *objspace) +{ + GC_ASSERT(during_gc != 0); + + objspace->profile.sweeping_time_ns += gc_clock_end(&objspace->profile.sweeping_start_time); +} + static void * gc_with_gvl(void *ptr) { @@ -11152,6 +11182,8 @@ gc_latest_gc_info(rb_execution_context_t *ec, VALUE self, VALUE arg) enum gc_stat_sym { gc_stat_sym_count, gc_stat_sym_time, + gc_stat_sym_marking_time, + gc_stat_sym_sweeping_time, gc_stat_sym_heap_allocated_pages, gc_stat_sym_heap_sorted_length, gc_stat_sym_heap_allocatable_pages, @@ -11201,6 +11233,8 @@ setup_gc_stat_symbols(void) #define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s)) S(count); S(time); + S(marking_time), + S(sweeping_time), S(heap_allocated_pages); S(heap_sorted_length); S(heap_allocatable_pages); @@ -11242,6 +11276,12 @@ setup_gc_stat_symbols(void) } } +static uint64_t +ns_to_ms(uint64_t ns) +{ + return ns / (1000 * 1000); +} + static size_t gc_stat_internal(VALUE hash_or_sym) { @@ -11267,7 +11307,9 @@ gc_stat_internal(VALUE hash_or_sym) rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr)); SET(count, objspace->profile.count); - SET(time, (size_t) (objspace->profile.total_time_ns / (1000 * 1000) /* ns -> ms */)); // TODO: UINT64T2NUM + SET(time, (size_t)ns_to_ms(objspace->profile.marking_time_ns + objspace->profile.sweeping_time_ns)); // TODO: UINT64T2NUM + SET(marking_time, (size_t)ns_to_ms(objspace->profile.marking_time_ns)); + SET(sweeping_time, (size_t)ns_to_ms(objspace->profile.sweeping_time_ns)); /* implementation dependent counters */ SET(heap_allocated_pages, heap_allocated_pages); |