139139#define major_gc_diag (...) (void)0
140140#endif
141141
142+ /* Logs the heap-growth gate decisions (gc_marks_finish + gc_sweep_finish_heap)
143+ * so a serial vs. parallel-sweep run can be diffed. Compile with
144+ * -DGROWTH_DIAG=1 to enable. */
145+ #ifndef GROWTH_DIAG
146+ #define GROWTH_DIAG 0
147+ #endif
148+ #if GROWTH_DIAG
149+ #define growth_diag (...) fprintf(stderr, __VA_ARGS__)
150+ #else
151+ #define growth_diag (...) (void)0
152+ #endif
153+
142154#ifndef PSWEEP_LOCK_STATS
143155#define PSWEEP_LOCK_STATS 0
144156#endif
@@ -5482,6 +5494,13 @@ gc_sweep_finish_heap(rb_objspace_t *objspace, rb_heap_t *heap)
54825494 }
54835495#endif
54845496
5497+ #if GROWTH_DIAG
5498+ const char * gd_decision = "no_growth_gate" ;
5499+ size_t gd_swept_initial = swept_slots ;
5500+ size_t gd_alloc_before = objspace -> heap_pages .allocatable_bytes ;
5501+ size_t gd_resurrected = 0 ;
5502+ #endif
5503+
54855504 if (swept_slots < min_free_slots &&
54865505 /* The heap is a growth heap if it freed more slots than had empty slots. */
54875506 ((heap -> empty_slots == 0 && total_slots > 0 ) || heap -> freed_slots > heap -> empty_slots )) {
@@ -5496,6 +5515,10 @@ gc_sweep_finish_heap(rb_objspace_t *objspace, rb_heap_t *heap)
54965515 heap_add_freepage (heap , resurrected_page , "gc_sweep_finish_heap" );
54975516
54985517 swept_slots += resurrected_page -> free_slots ;
5518+ #if GROWTH_DIAG
5519+ gd_resurrected ++ ;
5520+ gd_decision = "resurrect" ;
5521+ #endif
54995522 }
55005523
55015524 if (swept_slots < min_free_slots ) {
@@ -5505,15 +5528,32 @@ gc_sweep_finish_heap(rb_objspace_t *objspace, rb_heap_t *heap)
55055528 objspace -> profile .count - objspace -> rgengc .last_major_gc < RVALUE_OLD_AGE ) {
55065529 if (objspace -> heap_pages .allocatable_bytes < min_free_slots * heap -> slot_size ) {
55075530 heap_allocatable_bytes_expand (objspace , heap , swept_slots , heap -> total_slots , heap -> slot_size );
5531+ #if GROWTH_DIAG
5532+ gd_decision = "expand" ;
5533+ #endif
55085534 }
55095535 }
55105536 else if (swept_slots < min_free_slots * 7 / 8 &&
55115537 objspace -> heap_pages .allocatable_bytes < (min_free_slots * 7 / 8 - swept_slots ) * heap -> slot_size ) {
55125538 gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE ;
55135539 heap -> force_major_gc_count ++ ;
5540+ #if GROWTH_DIAG
5541+ gd_decision = "force_major_nofree" ;
5542+ #endif
55145543 }
55155544 }
55165545 }
5546+
5547+ growth_diag ("[growth_diag] site=sweep_finish_heap psweep=%d heap=%d reason=0x%x count=%zu "
5548+ "since_major=%zd full_mark=%d total_slots=%zu swept=%zu(freed=%zu+empty=%zu) "
5549+ "min_free=%zu alloc_bytes=%zu->%zu resurrected=%zu decision=%s\n" ,
5550+ USE_PARALLEL_SWEEP , (int )(heap - heaps ), objspace -> profile .latest_gc_info ,
5551+ objspace -> profile .count ,
5552+ (ssize_t )(objspace -> profile .count - objspace -> rgengc .last_major_gc ),
5553+ is_full_marking (objspace ), total_slots , gd_swept_initial ,
5554+ heap -> freed_slots , heap -> empty_slots , min_free_slots ,
5555+ gd_alloc_before , objspace -> heap_pages .allocatable_bytes ,
5556+ gd_resurrected , gd_decision );
55175557}
55185558
55195559static void
@@ -7542,14 +7582,35 @@ gc_marks_finish(rb_objspace_t *objspace)
75427582 heap_pages_freeable_pages = 0 ;
75437583 }
75447584
7585+ #if GROWTH_DIAG
7586+ const char * gd_decision ;
7587+ int gd_full_entry = full_marking ;
7588+ size_t gd_alloc_before = objspace -> heap_pages .allocatable_bytes ;
7589+ if (objspace -> heap_pages .allocatable_bytes != 0 ) {
7590+ gd_decision = "closed:alloc_bytes_nonzero" ;
7591+ }
7592+ else if (sweep_slots >= min_free_slots ) {
7593+ gd_decision = "closed:enough_free" ;
7594+ }
7595+ else {
7596+ gd_decision = "open:noop" ;
7597+ }
7598+ #endif
7599+
75457600 if (objspace -> heap_pages .allocatable_bytes == 0 && sweep_slots < min_free_slots ) {
75467601 if (!full_marking && sweep_slots < min_free_slots * 7 / 8 ) {
75477602 if (objspace -> profile .count - objspace -> rgengc .last_major_gc < RVALUE_OLD_AGE ) {
75487603 full_marking = TRUE;
7604+ #if GROWTH_DIAG
7605+ gd_decision = "promote_to_full" ;
7606+ #endif
75497607 }
75507608 else {
75517609 gc_report (1 , objspace , "gc_marks_finish: next is full GC!!)\n" );
75527610 gc_needs_major_flags |= GPR_FLAG_MAJOR_BY_NOFREE ;
7611+ #if GROWTH_DIAG
7612+ gd_decision = "force_major_nofree" ;
7613+ #endif
75537614 }
75547615 }
75557616
@@ -7562,9 +7623,21 @@ gc_marks_finish(rb_objspace_t *objspace)
75627623 /*size_t avg_slot_size = total_slots > 0 ? total_heap_bytes / total_slots : (size_t)heaps[0].slot_size;*/
75637624 /*heap_allocatable_bytes_expand(objspace, NULL, sweep_slots, total_slots, avg_slot_size);*/
75647625 heap_allocatable_bytes_expand (objspace , NULL , sweep_slots , total_slots , heaps [0 ].slot_size );
7626+ #if GROWTH_DIAG
7627+ if (gd_full_entry ) gd_decision = "expand" ;
7628+ #endif
75657629 }
75667630 }
75677631
7632+ growth_diag ("[growth_diag] site=marks_finish psweep=%d reason=0x%x count=%zu since_major=%zd "
7633+ "full_mark=%d total_slots=%zu sweep_slots=%zu min_free=%zu max_free=%zu "
7634+ "freeable_pages=%zu alloc_bytes=%zu->%zu decision=%s\n" ,
7635+ USE_PARALLEL_SWEEP , objspace -> profile .latest_gc_info , objspace -> profile .count ,
7636+ (ssize_t )(objspace -> profile .count - objspace -> rgengc .last_major_gc ),
7637+ gd_full_entry , total_slots , sweep_slots , min_free_slots , max_free_slots ,
7638+ heap_pages_freeable_pages , gd_alloc_before , objspace -> heap_pages .allocatable_bytes ,
7639+ gd_decision );
7640+
75687641 if (full_marking ) {
75697642 /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */
75707643 const double r = gc_params .oldobject_limit_factor ;
0 commit comments