Skip to content

Commit 7cde9aa

Browse files
authored
GC: Fix survival rates for LOH/POH (#100716)
The survival rate calculation for BGCs includes objects allocated during the BGC in the numerator but not denominator. This fixes that by adding them to size_before. The existing counters are reset mid-BGC, so the mark and sweep phases need to be added at separate points. I verified the fix over several gcperfsim runs and watched the counters getting updated in the debugger. This includes some new and moved comments from going through the phases of BGC and putting similarly ifdef-ed code together. Partial fix for #100594
1 parent 28c39bc commit 7cde9aa

File tree

3 files changed

+96
-70
lines changed

3 files changed

+96
-70
lines changed

src/coreclr/gc/gc.cpp

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ int relative_index_power2_free_space (size_t power2)
388388

389389
#ifdef BACKGROUND_GC
390390
uint32_t bgc_alloc_spin_count = 140;
391-
uint32_t bgc_alloc_spin_count_loh = 16;
391+
uint32_t bgc_alloc_spin_count_uoh = 16;
392392
uint32_t bgc_alloc_spin = 2;
393393

394394
inline
@@ -2657,13 +2657,10 @@ size_t gc_heap::end_loh_size = 0;
26572657
size_t gc_heap::bgc_begin_poh_size = 0;
26582658
size_t gc_heap::end_poh_size = 0;
26592659

2660+
size_t gc_heap::uoh_a_no_bgc[uoh_generation_count] = {};
2661+
size_t gc_heap::uoh_a_bgc_marking[uoh_generation_count] = {};
2662+
size_t gc_heap::uoh_a_bgc_planning[uoh_generation_count] = {};
26602663
#ifdef BGC_SERVO_TUNING
2661-
uint64_t gc_heap::loh_a_no_bgc = 0;
2662-
2663-
uint64_t gc_heap::loh_a_bgc_marking = 0;
2664-
2665-
uint64_t gc_heap::loh_a_bgc_planning = 0;
2666-
26672664
size_t gc_heap::bgc_maxgen_end_fl_size = 0;
26682665
#endif //BGC_SERVO_TUNING
26692666

@@ -2794,9 +2791,9 @@ FinalizerWorkItem* gc_heap::finalizer_work;
27942791
BOOL gc_heap::proceed_with_gc_p = FALSE;
27952792
GCSpinLock gc_heap::gc_lock;
27962793

2797-
#ifdef BGC_SERVO_TUNING
2798-
uint64_t gc_heap::total_loh_a_last_bgc = 0;
2799-
#endif //BGC_SERVO_TUNING
2794+
#ifdef BACKGROUND_GC
2795+
uint64_t gc_heap::total_uoh_a_last_bgc = 0;
2796+
#endif //BACKGROUND_GC
28002797

28012798
#ifdef USE_REGIONS
28022799
region_free_list gc_heap::global_regions_to_decommit[count_free_region_kinds];
@@ -15039,10 +15036,13 @@ gc_heap::init_gc_heap (int h_number)
1503915036
make_mark_stack(arr);
1504015037

1504115038
#ifdef BACKGROUND_GC
15039+
for (int i = uoh_start_generation; i < total_generation_count; i++)
15040+
{
15041+
uoh_a_no_bgc[i - uoh_start_generation] = 0;
15042+
uoh_a_bgc_marking[i - uoh_start_generation] = 0;
15043+
uoh_a_bgc_planning[i - uoh_start_generation] = 0;
15044+
}
1504215045
#ifdef BGC_SERVO_TUNING
15043-
loh_a_no_bgc = 0;
15044-
loh_a_bgc_marking = 0;
15045-
loh_a_bgc_planning = 0;
1504615046
bgc_maxgen_end_fl_size = 0;
1504715047
#endif //BGC_SERVO_TUNING
1504815048
freeable_soh_segment = 0;
@@ -18424,6 +18424,29 @@ bool gc_heap::should_retry_other_heap (int gen_number, size_t size)
1842418424
}
1842518425
}
1842618426

18427+
void gc_heap::bgc_record_uoh_allocation(int gen_number, size_t size)
18428+
{
18429+
assert((gen_number >= uoh_start_generation) && (gen_number < total_generation_count));
18430+
18431+
if (gc_heap::background_running_p())
18432+
{
18433+
background_uoh_alloc_count++;
18434+
18435+
if (current_c_gc_state == c_gc_state_planning)
18436+
{
18437+
uoh_a_bgc_planning[gen_number - uoh_start_generation] += size;
18438+
}
18439+
else
18440+
{
18441+
uoh_a_bgc_marking[gen_number - uoh_start_generation] += size;
18442+
}
18443+
}
18444+
else
18445+
{
18446+
uoh_a_no_bgc[gen_number - uoh_start_generation] += size;
18447+
}
18448+
}
18449+
1842718450
allocation_state gc_heap::allocate_uoh (int gen_number,
1842818451
size_t size,
1842918452
alloc_context* acontext,
@@ -18446,26 +18469,12 @@ allocation_state gc_heap::allocate_uoh (int gen_number,
1844618469
#endif //RECORD_LOH_STATE
1844718470

1844818471
#ifdef BACKGROUND_GC
18472+
bgc_record_uoh_allocation(gen_number, size);
18473+
1844918474
if (gc_heap::background_running_p())
1845018475
{
18451-
#ifdef BGC_SERVO_TUNING
18452-
bool planning_p = (current_c_gc_state == c_gc_state_planning);
18453-
#endif //BGC_SERVO_TUNING
18454-
18455-
background_uoh_alloc_count++;
18456-
//if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
18476+
//if ((background_uoh_alloc_count % bgc_alloc_spin_count_uoh) == 0)
1845718477
{
18458-
#ifdef BGC_SERVO_TUNING
18459-
if (planning_p)
18460-
{
18461-
loh_a_bgc_planning += size;
18462-
}
18463-
else
18464-
{
18465-
loh_a_bgc_marking += size;
18466-
}
18467-
#endif //BGC_SERVO_TUNING
18468-
1846918478
int spin_for_allocation = (gen_number == loh_generation) ?
1847018479
bgc_loh_allocate_spin() :
1847118480
bgc_poh_allocate_spin();
@@ -18491,12 +18500,6 @@ allocation_state gc_heap::allocate_uoh (int gen_number,
1849118500
}
1849218501
}
1849318502
}
18494-
#ifdef BGC_SERVO_TUNING
18495-
else
18496-
{
18497-
loh_a_no_bgc += size;
18498-
}
18499-
#endif //BGC_SERVO_TUNING
1850018503
#endif //BACKGROUND_GC
1850118504

1850218505
gc_reason gr = reason_oos_loh;
@@ -40624,7 +40627,7 @@ void gc_heap::bgc_tuning::record_and_adjust_bgc_end()
4062440627

4062540628
calculate_tuning (max_generation, true);
4062640629

40627-
if (total_loh_a_last_bgc > 0)
40630+
if (total_uoh_a_last_bgc > 0)
4062840631
{
4062940632
calculate_tuning (loh_generation, true);
4063040633
}
@@ -45821,9 +45824,6 @@ void gc_heap::background_sweep()
4582145824
concurrent_print_time_delta ("Sw");
4582245825
dprintf (2, ("---- (GC%zu)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
4582345826

45824-
//block concurrent allocation for large objects
45825-
dprintf (3, ("lh state: planning"));
45826-
4582745827
for (int i = 0; i <= max_generation; i++)
4582845828
{
4582945829
generation* gen_to_reset = generation_of (i);
@@ -45872,6 +45872,9 @@ void gc_heap::background_sweep()
4587245872
sweep_ro_segments();
4587345873
#endif //FEATURE_BASICFREEZE
4587445874

45875+
dprintf (3, ("lh state: planning"));
45876+
45877+
// Multiple threads may reach here. This conditional partially avoids multiple volatile writes.
4587545878
if (current_c_gc_state != c_gc_state_planning)
4587645879
{
4587745880
current_c_gc_state = c_gc_state_planning;
@@ -45902,9 +45905,7 @@ void gc_heap::background_sweep()
4590245905

4590345906
if (heap_number == 0)
4590445907
{
45905-
#ifdef BGC_SERVO_TUNING
45906-
get_and_reset_loh_alloc_info();
45907-
#endif //BGC_SERVO_TUNING
45908+
get_and_reset_uoh_alloc_info();
4590845909
uint64_t suspended_end_ts = GetHighPrecisionTimeStamp();
4590945910
last_bgc_info[last_bgc_info_index].pause_durations[1] = (size_t)(suspended_end_ts - suspended_start_time);
4591045911
total_suspended_time += last_bgc_info[last_bgc_info_index].pause_durations[1];
@@ -46233,6 +46234,7 @@ void gc_heap::background_sweep()
4623346234
concurrent_print_time_delta ("Swe SOH");
4623446235
FIRE_EVENT(BGC1stSweepEnd, 0);
4623546236

46237+
//block concurrent allocation for UOH objects
4623646238
enter_spin_lock (&more_space_lock_uoh);
4623746239
add_saved_spinlock_info (true, me_acquire, mt_bgc_uoh_sweep, msl_entered);
4623846240

@@ -46288,6 +46290,15 @@ void gc_heap::background_sweep()
4628846290
// be accurate.
4628946291
compute_new_dynamic_data (max_generation);
4629046292

46293+
// We also need to adjust size_before for UOH allocations that occurred during sweeping.
46294+
gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
46295+
for (int i = uoh_start_generation; i < total_generation_count; i++)
46296+
{
46297+
assert(uoh_a_bgc_marking[i - uoh_start_generation] == 0);
46298+
assert(uoh_a_no_bgc[i - uoh_start_generation] == 0);
46299+
current_gc_data_per_heap->gen_data[i].size_before += uoh_a_bgc_planning[i - uoh_start_generation];
46300+
}
46301+
4629146302
#ifdef DOUBLY_LINKED_FL
4629246303
current_bgc_state = bgc_not_in_process;
4629346304

@@ -50257,17 +50268,15 @@ void gc_heap::check_and_adjust_bgc_tuning (int gen_number, size_t physical_size,
5025750268
}
5025850269
}
5025950270
}
50271+
#endif //BGC_SERVO_TUNING
5026050272

50261-
void gc_heap::get_and_reset_loh_alloc_info()
50273+
void gc_heap::get_and_reset_uoh_alloc_info()
5026250274
{
50263-
if (!bgc_tuning::enable_fl_tuning)
50264-
return;
50275+
total_uoh_a_last_bgc = 0;
5026550276

50266-
total_loh_a_last_bgc = 0;
50267-
50268-
uint64_t total_loh_a_no_bgc = 0;
50269-
uint64_t total_loh_a_bgc_marking = 0;
50270-
uint64_t total_loh_a_bgc_planning = 0;
50277+
uint64_t total_uoh_a_no_bgc = 0;
50278+
uint64_t total_uoh_a_bgc_marking = 0;
50279+
uint64_t total_uoh_a_bgc_planning = 0;
5027150280
#ifdef MULTIPLE_HEAPS
5027250281
for (int i = 0; i < gc_heap::n_heaps; i++)
5027350282
{
@@ -50276,21 +50285,32 @@ void gc_heap::get_and_reset_loh_alloc_info()
5027650285
{
5027750286
gc_heap* hp = pGenGCHeap;
5027850287
#endif //MULTIPLE_HEAPS
50279-
total_loh_a_no_bgc += hp->loh_a_no_bgc;
50280-
hp->loh_a_no_bgc = 0;
50281-
total_loh_a_bgc_marking += hp->loh_a_bgc_marking;
50282-
hp->loh_a_bgc_marking = 0;
50283-
total_loh_a_bgc_planning += hp->loh_a_bgc_planning;
50284-
hp->loh_a_bgc_planning = 0;
50288+
50289+
// We need to adjust size_before for UOH allocations that occurred during marking
50290+
// before we lose the values here.
50291+
gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
50292+
// loh/poh_a_bgc_planning should be the same as they were when init_records set size_before.
50293+
for (int i = uoh_start_generation; i < total_generation_count; i++)
50294+
{
50295+
current_gc_data_per_heap->gen_data[i].size_before += hp->uoh_a_bgc_marking[i - uoh_start_generation];
50296+
50297+
total_uoh_a_no_bgc += hp->uoh_a_no_bgc[i - uoh_start_generation];
50298+
hp->uoh_a_no_bgc[i - uoh_start_generation] = 0;
50299+
50300+
total_uoh_a_bgc_marking += hp->uoh_a_bgc_marking[i - uoh_start_generation];
50301+
hp->uoh_a_bgc_marking[i - uoh_start_generation] = 0;
50302+
50303+
total_uoh_a_bgc_planning += hp->uoh_a_bgc_planning[i - uoh_start_generation];
50304+
hp->uoh_a_bgc_planning[i - uoh_start_generation] = 0;
50305+
}
5028550306
}
5028650307
dprintf (2, ("LOH alloc: outside bgc: %zd; bm: %zd; bp: %zd",
50287-
total_loh_a_no_bgc,
50288-
total_loh_a_bgc_marking,
50289-
total_loh_a_bgc_planning));
50308+
total_uoh_a_no_bgc,
50309+
total_uoh_a_bgc_marking,
50310+
total_uoh_a_bgc_planning));
5029050311

50291-
total_loh_a_last_bgc = total_loh_a_no_bgc + total_loh_a_bgc_marking + total_loh_a_bgc_planning;
50312+
total_uoh_a_last_bgc = total_uoh_a_no_bgc + total_uoh_a_bgc_marking + total_uoh_a_bgc_planning;
5029250313
}
50293-
#endif //BGC_SERVO_TUNING
5029450314

5029550315
bool gc_heap::is_pm_ratio_exceeded()
5029650316
{

src/coreclr/gc/gc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ enum gc_generation_num
124124
ephemeral_generation_count = max_generation,
125125

126126
// number of all generations
127-
total_generation_count = poh_generation + 1
127+
total_generation_count = poh_generation + 1,
128+
129+
// number of uoh generations
130+
uoh_generation_count = total_generation_count - uoh_start_generation
128131
};
129132

130133
#ifdef GC_CONFIG_DRIVEN

src/coreclr/gc/gcpriv.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,8 @@ class gc_heap
17521752

17531753
PER_HEAP_ISOLATED_METHOD void add_to_history();
17541754

1755+
PER_HEAP_ISOLATED_METHOD void get_and_reset_uoh_alloc_info();
1756+
17551757
#ifdef BGC_SERVO_TUNING
17561758
// Currently BGC servo tuning is an experimental feature.
17571759
class bgc_tuning
@@ -1997,7 +1999,6 @@ class gc_heap
19971999
};
19982000

19992001
PER_HEAP_ISOLATED_METHOD void check_and_adjust_bgc_tuning (int gen_number, size_t physical_size, ptrdiff_t virtual_fl_size);
2000-
PER_HEAP_ISOLATED_METHOD void get_and_reset_loh_alloc_info();
20012002
#endif //BGC_SERVO_TUNING
20022003

20032004
#ifndef USE_REGIONS
@@ -2230,6 +2231,8 @@ class gc_heap
22302231
PER_HEAP_METHOD BOOL bgc_loh_allocate_spin();
22312232

22322233
PER_HEAP_METHOD BOOL bgc_poh_allocate_spin();
2234+
2235+
PER_HEAP_METHOD void bgc_record_uoh_allocation(int gen_number, size_t size);
22332236
#endif //BACKGROUND_GC
22342237

22352238
PER_HEAP_METHOD void add_saved_spinlock_info (
@@ -3436,6 +3439,11 @@ class gc_heap
34363439

34373440
PER_HEAP_FIELD_SINGLE_GC uint8_t* next_sweep_obj;
34383441
PER_HEAP_FIELD_SINGLE_GC uint8_t* current_sweep_pos;
3442+
3443+
PER_HEAP_FIELD_SINGLE_GC size_t uoh_a_no_bgc[uoh_generation_count];
3444+
PER_HEAP_FIELD_SINGLE_GC size_t uoh_a_bgc_marking[uoh_generation_count];
3445+
PER_HEAP_FIELD_SINGLE_GC size_t uoh_a_bgc_planning[uoh_generation_count];
3446+
34393447
#ifdef DOUBLY_LINKED_FL
34403448
PER_HEAP_FIELD_SINGLE_GC heap_segment* current_sweep_seg;
34413449
#endif //DOUBLY_LINKED_FL
@@ -3461,9 +3469,6 @@ class gc_heap
34613469
#endif //SNOOP_STATS
34623470

34633471
#ifdef BGC_SERVO_TUNING
3464-
PER_HEAP_FIELD_SINGLE_GC uint64_t loh_a_no_bgc;
3465-
PER_HEAP_FIELD_SINGLE_GC uint64_t loh_a_bgc_marking;
3466-
PER_HEAP_FIELD_SINGLE_GC uint64_t loh_a_bgc_planning;
34673472
PER_HEAP_FIELD_SINGLE_GC size_t bgc_maxgen_end_fl_size;
34683473
#endif //BGC_SERVO_TUNING
34693474
#endif //BACKGROUND_GC
@@ -4097,11 +4102,9 @@ class gc_heap
40974102

40984103
PER_HEAP_ISOLATED_FIELD_SINGLE_GC GCEvent bgc_start_event;
40994104

4100-
#ifdef BGC_SERVO_TUNING
41014105
// Total allocated last BGC's plan + between last and this bgc +
41024106
// this bgc's mark
4103-
PER_HEAP_ISOLATED_FIELD_SINGLE_GC uint64_t total_loh_a_last_bgc;
4104-
#endif //BGC_SERVO_TUNING
4107+
PER_HEAP_ISOLATED_FIELD_SINGLE_GC uint64_t total_uoh_a_last_bgc;
41054108
#endif //BACKGROUND_GC
41064109

41074110
#ifdef USE_REGIONS

0 commit comments

Comments
 (0)