From 74d008ac59c715e5b692890d649109e14c64666a Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Fri, 17 Apr 2020 15:53:42 +0200 Subject: [PATCH 01/12] Initial snap shot of decommit optimization code. Key ideas: - in decommit_ephemeral_segment_pages, instead of decommitting right there, just remember what we intended to decommit. - in gc_thread function, have the thread with heap_number 0 periodically wake up and do gradual decommits for all heaps by calling the new method decommit_ephemeral_pages_step on them. - decommit_ephemeral_pages_step will decommit a "reasonable" amount of space per heap and return how much it decomitted. A return value of 0 indicates no further progress. --- src/coreclr/src/gc/gc.cpp | 59 ++++++++++++++++++++++++++++++++++++- src/coreclr/src/gc/gcpriv.h | 17 +++++++++-- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 3e3096e604eae5..331b5b5ea5888f 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -2112,6 +2112,7 @@ int* gc_heap::g_mark_stack_busy; size_t* gc_heap::g_bpromoted; #endif //BACKGROUND_GC +BOOL gc_heap::gradual_decommit_in_progress_p = FALSE; #else //MULTIPLE_HEAPS size_t gc_heap::g_promoted; @@ -5398,7 +5399,19 @@ void gc_heap::gc_thread_function () if (heap_number == 0) { - gc_heap::ee_suspend_event.Wait(INFINITE, FALSE); + uint32_t wait_result = gc_heap::ee_suspend_event.Wait(gradual_decommit_in_progress_p ? 100 : INFINITE, FALSE); + if (wait_result == WAIT_TIMEOUT) + { + size_t decommit_size = 0; + for (int i = 0; i < n_heaps; i++) + { + gc_heap* hp = gc_heap::g_heaps[i]; + decommit_size += hp->decommit_ephemeral_pages_step (); + } + if (decommit_size == 0) + gradual_decommit_in_progress_p = false; + continue; + } BEGIN_TIMING(suspend_ee_during_log); GCToEEInterface::SuspendEE(SUSPEND_FOR_GC); @@ -31808,14 +31821,58 @@ void gc_heap::decommit_ephemeral_segment_pages() #endif // HOST_64BIT slack_space = min (slack_space, new_slack_space); +#ifdef MULTIPLE_HEAPS + heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_allocated (ephemeral_heap_segment) + slack_space; + if (heap_segment_decommit_target (ephemeral_heap_segment) < heap_segment_committed (ephemeral_heap_segment)) + gradual_decommit_in_progress_p = !use_large_pages_p; +#endif //MULTIPLE_HEAPS } +#ifdef MULTIPLE_HEAPS + else + { + // for a gen 0, revise the decommit target if it's lower than what we think we'll need + slack_space = dd_desired_allocation (dd); + if (heap_segment_decommit_target (ephemeral_heap_segment) < heap_segment_allocated (ephemeral_heap_segment) + slack_space) + heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_allocated (ephemeral_heap_segment) + slack_space; + } +#endif //MULTIPLE_HEAPS +#ifndef MULTIPLE_HEAPS decommit_heap_segment_pages (ephemeral_heap_segment, slack_space); +#endif // !MULTIPLE_HEAPS gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment); } +#ifdef MULTIPLE_HEAPS +size_t gc_heap::decommit_ephemeral_pages_step () +{ + uint8_t* decommit_target = heap_segment_decommit_target (ephemeral_heap_segment); + const size_t EXTRA_SPACE = 32 * OS_PAGE_SIZE; + decommit_target += EXTRA_SPACE; + uint8_t* committed = heap_segment_committed(ephemeral_heap_segment); + if (decommit_target < committed) + { + const size_t DECOMMIT_STEP_SIZE = min (4096 / n_heaps, 100) * OS_PAGE_SIZE; + uint8_t* page_start = align_on_page ( max (decommit_target, committed - DECOMMIT_STEP_SIZE) ); + size_t size = committed - page_start; + virtual_decommit(page_start, size, heap_number); + dprintf(3, ("Decommitting heap segment [%Ix, %Ix[(%d)", + (size_t)page_start, + (size_t)(page_start + size), + size)); + heap_segment_committed (ephemeral_heap_segment) = page_start; + if (heap_segment_used (ephemeral_heap_segment) > heap_segment_committed (ephemeral_heap_segment)) + { + heap_segment_used (ephemeral_heap_segment) = heap_segment_committed (ephemeral_heap_segment); + } + return size; + } + return 0; +} +#endif //MULTIPLE_HEAPS + //This is meant to be called by decide_on_compacting. size_t gc_heap::generation_fragmentation (generation* gen, diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index 8b7765d20e3744..f2380c836733b3 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -1651,6 +1651,8 @@ class gc_heap PER_HEAP void decommit_heap_segment_pages (heap_segment* seg, size_t extra_space); PER_HEAP + size_t decommit_ephemeral_pages_step(); + PER_HEAP void decommit_heap_segment (heap_segment* seg); PER_HEAP_ISOLATED bool virtual_alloc_commit_for_heap (void* addr, size_t size, int h_number); @@ -2450,8 +2452,6 @@ class gc_heap void check_loh_compact_mode (BOOL all_heaps_compacted_p); #endif //FEATURE_LOH_COMPACTION - PER_HEAP - void decommit_ephemeral_segment_pages (int condemned_gen_number); PER_HEAP void fix_generation_bounds (int condemned_gen_number, generation* consing_gen); @@ -3788,6 +3788,11 @@ class gc_heap PER_HEAP_ISOLATED BOOL proceed_with_gc_p; +#ifdef MULTIPLE_HEAPS + PER_HEAP_ISOLATED + BOOL gradual_decommit_in_progress_p; +#endif //MULTIPLE_HEAPS + #define youngest_generation (generation_of (0)) #define large_object_generation (generation_of (loh_generation)) #define pinned_object_generation (generation_of (poh_generation)) @@ -4702,6 +4707,7 @@ class heap_segment uint8_t* background_allocated; #ifdef MULTIPLE_HEAPS gc_heap* heap; + uint8_t* decommit_target; #endif //MULTIPLE_HEAPS uint8_t* plan_allocated; uint8_t* saved_bg_allocated; @@ -4738,6 +4744,13 @@ uint8_t*& heap_segment_committed (heap_segment* inst) { return inst->committed; } +#ifdef MULTIPLE_HEAPS +inline +uint8_t*& heap_segment_decommit_target (heap_segment* inst) +{ + return inst->decommit_target; +} +#endif //MULTIPLE_HEAPS inline uint8_t*& heap_segment_used (heap_segment* inst) { From 308b1480ac35b858760de1d2552dbee200a05a8a Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Mon, 20 Apr 2020 16:18:07 +0200 Subject: [PATCH 02/12] Fixed issues with the gradual decommit logic. Details: - add code checking the assumption that nobody is changing the committed size or the desired allocation size while we do the decommitting - fix logic error causing race between committing and decommitting - add a decommit step at the end of GC to make sure decommit would still happen even for very frequent GCs. - fix issue that crept in during pinned heap work --- src/coreclr/src/gc/gc.cpp | 66 +++++++++++++++++++++++++++---------- src/coreclr/src/gc/gcpriv.h | 6 +++- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 331b5b5ea5888f..b0b44345f27a36 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -5399,17 +5399,11 @@ void gc_heap::gc_thread_function () if (heap_number == 0) { - uint32_t wait_result = gc_heap::ee_suspend_event.Wait(gradual_decommit_in_progress_p ? 100 : INFINITE, FALSE); + const int DECOMMIT_TIME_STEP = 100; // milliseconds + uint32_t wait_result = gc_heap::ee_suspend_event.Wait(gradual_decommit_in_progress_p ? DECOMMIT_TIME_STEP : INFINITE, FALSE); if (wait_result == WAIT_TIMEOUT) { - size_t decommit_size = 0; - for (int i = 0; i < n_heaps; i++) - { - gc_heap* hp = gc_heap::g_heaps[i]; - decommit_size += hp->decommit_ephemeral_pages_step (); - } - if (decommit_size == 0) - gradual_decommit_in_progress_p = false; + gradual_decommit_in_progress_p = decommit_step (); continue; } @@ -5493,6 +5487,12 @@ void gc_heap::gc_thread_function () hp->set_gc_done(); } } + + // check if we should do some decommitting + if (gradual_decommit_in_progress_p) + { + gradual_decommit_in_progress_p = decommit_step (); + } } else { @@ -16304,9 +16304,9 @@ void gc_heap::gc1() int limit = settings.condemned_generation; if (limit == max_generation) { - limit = total_generation_count; + limit = total_generation_count-1; } - for (int gen = 0; gen < limit; gen++) + for (int gen = 0; gen <= limit; gen++) { size_t total_desired = 0; @@ -31822,9 +31822,16 @@ void gc_heap::decommit_ephemeral_segment_pages() slack_space = min (slack_space, new_slack_space); #ifdef MULTIPLE_HEAPS - heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_allocated (ephemeral_heap_segment) + slack_space; - if (heap_segment_decommit_target (ephemeral_heap_segment) < heap_segment_committed (ephemeral_heap_segment)) - gradual_decommit_in_progress_p = !use_large_pages_p; + uint8_t *decommit_target = heap_segment_allocated (ephemeral_heap_segment) + slack_space; + if ((decommit_target < heap_segment_committed (ephemeral_heap_segment)) && !use_large_pages_p) + { + gradual_decommit_in_progress_p = TRUE; + heap_segment_decommit_target (ephemeral_heap_segment) = decommit_target; + } + else + { + heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_reserved (ephemeral_heap_segment); + } #endif //MULTIPLE_HEAPS } #ifdef MULTIPLE_HEAPS @@ -31835,6 +31842,8 @@ void gc_heap::decommit_ephemeral_segment_pages() if (heap_segment_decommit_target (ephemeral_heap_segment) < heap_segment_allocated (ephemeral_heap_segment) + slack_space) heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_allocated (ephemeral_heap_segment) + slack_space; } + ephemeral_heap_segment->saved_committed = heap_segment_committed (ephemeral_heap_segment); + ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation (dd); #endif //MULTIPLE_HEAPS #ifndef MULTIPLE_HEAPS @@ -31846,14 +31855,36 @@ void gc_heap::decommit_ephemeral_segment_pages() } #ifdef MULTIPLE_HEAPS -size_t gc_heap::decommit_ephemeral_pages_step () +bool gc_heap::decommit_step () +{ + size_t decommit_size = 0; + for (int i = 0; i < n_heaps; i++) + { + gc_heap* hp = gc_heap::g_heaps[i]; + decommit_size += hp->decommit_ephemeral_segment_pages_step (); + } + return (decommit_size != 0); +} + +size_t gc_heap::decommit_ephemeral_segment_pages_step () { + // we rely on desired allocation not being changed outside of GC + if (ephemeral_heap_segment->saved_desired_allocation != dd_desired_allocation(dynamic_data_of(0))) + { + GCToOSInterface::DebugBreak(); + } uint8_t* decommit_target = heap_segment_decommit_target (ephemeral_heap_segment); - const size_t EXTRA_SPACE = 32 * OS_PAGE_SIZE; + size_t EXTRA_SPACE = loh_size_threshold + 4 * OS_PAGE_SIZE; decommit_target += EXTRA_SPACE; - uint8_t* committed = heap_segment_committed(ephemeral_heap_segment); + uint8_t* committed = heap_segment_committed (ephemeral_heap_segment); if (decommit_target < committed) { + // we rely on other threads not messing with committed if we are about to trim it down + if (ephemeral_heap_segment->saved_committed != heap_segment_committed (ephemeral_heap_segment)) + { + GCToOSInterface::DebugBreak(); + } + const size_t DECOMMIT_STEP_SIZE = min (4096 / n_heaps, 100) * OS_PAGE_SIZE; uint8_t* page_start = align_on_page ( max (decommit_target, committed - DECOMMIT_STEP_SIZE) ); size_t size = committed - page_start; @@ -31863,6 +31894,7 @@ size_t gc_heap::decommit_ephemeral_pages_step () (size_t)(page_start + size), size)); heap_segment_committed (ephemeral_heap_segment) = page_start; + ephemeral_heap_segment->saved_committed = page_start; if (heap_segment_used (ephemeral_heap_segment) > heap_segment_committed (ephemeral_heap_segment)) { heap_segment_used (ephemeral_heap_segment) = heap_segment_committed (ephemeral_heap_segment); diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index f2380c836733b3..241cee488c7a07 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -1651,7 +1651,9 @@ class gc_heap PER_HEAP void decommit_heap_segment_pages (heap_segment* seg, size_t extra_space); PER_HEAP - size_t decommit_ephemeral_pages_step(); + size_t decommit_ephemeral_segment_pages_step (); + PER_HEAP_ISOLATED + bool decommit_step (); PER_HEAP void decommit_heap_segment (heap_segment* seg); PER_HEAP_ISOLATED @@ -4708,6 +4710,8 @@ class heap_segment #ifdef MULTIPLE_HEAPS gc_heap* heap; uint8_t* decommit_target; + uint8_t* saved_committed; + size_t saved_desired_allocation; #endif //MULTIPLE_HEAPS uint8_t* plan_allocated; uint8_t* saved_bg_allocated; From e828f25ae21d1b8ea9e86d86152d2e7f0d6b2e94 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Mon, 4 May 2020 12:41:48 +0200 Subject: [PATCH 03/12] Refinement to decommit policy taking gen1 budget and free list space into account. --- src/coreclr/src/gc/gc.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index b0b44345f27a36..39ec2168f19d4c 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -31786,35 +31786,46 @@ void gc_heap::decommit_ephemeral_segment_pages() size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment); - dynamic_data* dd = dynamic_data_of (0); + dynamic_data* dd0 = dynamic_data_of (0); #ifndef MULTIPLE_HEAPS size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024)); size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT); - size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time; + size_t ephemeral_elapsed = dd_time_clock(dd0) - gc_last_ephemeral_decommit_time; - if (dd_desired_allocation (dd) > gc_gen0_desired_high) + if (dd_desired_allocation (dd0) > gc_gen0_desired_high) { - gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space; + gc_gen0_desired_high = dd_desired_allocation (dd0) + extra_space; } if (ephemeral_elapsed >= decommit_timeout) { slack_space = min (slack_space, gc_gen0_desired_high); - gc_last_ephemeral_decommit_time = dd_time_clock(dd); + gc_last_ephemeral_decommit_time = dd_time_clock(dd0); gc_gen0_desired_high = 0; } #endif //!MULTIPLE_HEAPS + // this is how much we are going to allocate in gen 0 + ptrdiff_t desired_allocation = dd_desired_allocation (dd0); + + // estimate how we are going to need in gen 1 - estimate half the free list space gets used + dynamic_data* dd1 = dynamic_data_of (1); + ptrdiff_t desired_allocation_1 = dd_new_allocation (dd1) + (generation_free_list_space (generation_of (1)) / 2); + if (desired_allocation_1 > 0) + { + desired_allocation += desired_allocation_1; + } + if (settings.condemned_generation >= (max_generation-1)) { size_t new_slack_space = #ifdef HOST_64BIT - max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd)); + max(min(min(soh_segment_size/32, dd_max_size (dd0)), (generation_size (max_generation) / 10)), (size_t)desired_allocation); #else #ifdef FEATURE_CORECLR - dd_desired_allocation (dd); + desired_allocation; #else dd_max_size (dd); #endif //FEATURE_CORECLR @@ -31838,12 +31849,12 @@ void gc_heap::decommit_ephemeral_segment_pages() else { // for a gen 0, revise the decommit target if it's lower than what we think we'll need - slack_space = dd_desired_allocation (dd); + slack_space = desired_allocation; if (heap_segment_decommit_target (ephemeral_heap_segment) < heap_segment_allocated (ephemeral_heap_segment) + slack_space) heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_allocated (ephemeral_heap_segment) + slack_space; } ephemeral_heap_segment->saved_committed = heap_segment_committed (ephemeral_heap_segment); - ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation (dd); + ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation (dd0); #endif //MULTIPLE_HEAPS #ifndef MULTIPLE_HEAPS @@ -31885,7 +31896,7 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () GCToOSInterface::DebugBreak(); } - const size_t DECOMMIT_STEP_SIZE = min (4096 / n_heaps, 100) * OS_PAGE_SIZE; + const size_t DECOMMIT_STEP_SIZE = max (4096 / n_heaps, 100) * OS_PAGE_SIZE; uint8_t* page_start = align_on_page ( max (decommit_target, committed - DECOMMIT_STEP_SIZE) ); size_t size = committed - page_start; virtual_decommit(page_start, size, heap_number); From 18111aa359e6d44ecc68cf594bb1b14490181144 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Tue, 5 May 2020 14:29:22 +0200 Subject: [PATCH 04/12] Move consideration of loh_threshold to decommit_ephemeral_segment_pages. Fix bug in accounting for free list space. --- src/coreclr/src/gc/gc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 39ec2168f19d4c..fcc8942a3cf63e 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -31808,11 +31808,11 @@ void gc_heap::decommit_ephemeral_segment_pages() #endif //!MULTIPLE_HEAPS // this is how much we are going to allocate in gen 0 - ptrdiff_t desired_allocation = dd_desired_allocation (dd0); + ptrdiff_t desired_allocation = dd_desired_allocation (dd0) + loh_size_threshold; // estimate how we are going to need in gen 1 - estimate half the free list space gets used dynamic_data* dd1 = dynamic_data_of (1); - ptrdiff_t desired_allocation_1 = dd_new_allocation (dd1) + (generation_free_list_space (generation_of (1)) / 2); + ptrdiff_t desired_allocation_1 = dd_new_allocation (dd1) - (generation_free_list_space (generation_of (1)) / 2); if (desired_allocation_1 > 0) { desired_allocation += desired_allocation_1; @@ -31885,7 +31885,7 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () GCToOSInterface::DebugBreak(); } uint8_t* decommit_target = heap_segment_decommit_target (ephemeral_heap_segment); - size_t EXTRA_SPACE = loh_size_threshold + 4 * OS_PAGE_SIZE; + size_t EXTRA_SPACE = 4 * OS_PAGE_SIZE; decommit_target += EXTRA_SPACE; uint8_t* committed = heap_segment_committed (ephemeral_heap_segment); if (decommit_target < committed) From 4081dbed2f354a03ae3e7ee2cc7f79f2a973da5e Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Wed, 6 May 2020 16:57:35 +0200 Subject: [PATCH 05/12] Streamline logic in decommit_ephemeral_segment_pages, add exponential smoothing of the decommit target when ramping down, limit decommit rate for workstation GC. --- src/coreclr/src/gc/gc.cpp | 91 +++++++++++++++++-------------------- src/coreclr/src/gc/gcpriv.h | 4 +- 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index f94554869f823a..ad1d00171b03e7 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -31739,34 +31739,13 @@ void gc_heap::trim_youngest_desired_low_memory() void gc_heap::decommit_ephemeral_segment_pages() { - if (settings.concurrent) + if (settings.concurrent || use_large_pages_p) { return; } - size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment); - dynamic_data* dd0 = dynamic_data_of (0); -#ifndef MULTIPLE_HEAPS - size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024)); - size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT); - size_t ephemeral_elapsed = dd_time_clock(dd0) - gc_last_ephemeral_decommit_time; - - if (dd_desired_allocation (dd0) > gc_gen0_desired_high) - { - gc_gen0_desired_high = dd_desired_allocation (dd0) + extra_space; - } - - if (ephemeral_elapsed >= decommit_timeout) - { - slack_space = min (slack_space, gc_gen0_desired_high); - - gc_last_ephemeral_decommit_time = dd_time_clock(dd0); - gc_gen0_desired_high = 0; - } -#endif //!MULTIPLE_HEAPS - // this is how much we are going to allocate in gen 0 ptrdiff_t desired_allocation = dd_desired_allocation (dd0) + loh_size_threshold; @@ -31778,46 +31757,54 @@ void gc_heap::decommit_ephemeral_segment_pages() desired_allocation += desired_allocation_1; } - if (settings.condemned_generation >= (max_generation-1)) - { - size_t new_slack_space = + size_t slack_space = #ifdef HOST_64BIT - max(min(min(soh_segment_size/32, dd_max_size (dd0)), (generation_size (max_generation) / 10)), (size_t)desired_allocation); + max(min(min(soh_segment_size/32, dd_max_size (dd0)), (generation_size (max_generation) / 10)), (size_t)desired_allocation); #else #ifdef FEATURE_CORECLR - desired_allocation; + desired_allocation; #else - dd_max_size (dd); + dd_max_size (dd); #endif //FEATURE_CORECLR #endif // HOST_64BIT - slack_space = min (slack_space, new_slack_space); -#ifdef MULTIPLE_HEAPS - uint8_t *decommit_target = heap_segment_allocated (ephemeral_heap_segment) + slack_space; - if ((decommit_target < heap_segment_committed (ephemeral_heap_segment)) && !use_large_pages_p) - { - gradual_decommit_in_progress_p = TRUE; - heap_segment_decommit_target (ephemeral_heap_segment) = decommit_target; - } - else - { - heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_reserved (ephemeral_heap_segment); - } -#endif //MULTIPLE_HEAPS + uint8_t *decommit_target = heap_segment_allocated (ephemeral_heap_segment) + slack_space; + if (decommit_target < heap_segment_decommit_target (ephemeral_heap_segment)) + { + // we used to have a higher target - do exponential smoothing by computing + // essentially decommit_target = 1/3*decommit_target + 2/3*previous_decommit_target + // computation below is slightly different to avoid overflow + ptrdiff_t target_decrease = heap_segment_decommit_target (ephemeral_heap_segment) - decommit_target; + decommit_target += target_decrease * 2 / 3; } + + heap_segment_decommit_target(ephemeral_heap_segment) = decommit_target; + #ifdef MULTIPLE_HEAPS - else + if (decommit_target < heap_segment_committed (ephemeral_heap_segment)) { - // for a gen 0, revise the decommit target if it's lower than what we think we'll need - slack_space = desired_allocation; - if (heap_segment_decommit_target (ephemeral_heap_segment) < heap_segment_allocated (ephemeral_heap_segment) + slack_space) - heap_segment_decommit_target (ephemeral_heap_segment) = heap_segment_allocated (ephemeral_heap_segment) + slack_space; + gradual_decommit_in_progress_p = TRUE; } - ephemeral_heap_segment->saved_committed = heap_segment_committed (ephemeral_heap_segment); - ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation (dd0); -#endif //MULTIPLE_HEAPS + // these are only for checking against logic errors + ephemeral_heap_segment->saved_committed = heap_segment_committed(ephemeral_heap_segment); + ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation(dd0); +#endif // MULTIPLE_HEAPS #ifndef MULTIPLE_HEAPS + // we want to limit the amount of decommit we do per time to indirectly + // limit the amount of time spent in recommit and page faults + size_t ephemeral_elapsed = dd_time_clock (dd0) - gc_last_ephemeral_decommit_time; + gc_last_ephemeral_decommit_time = dd_time_clock (dd0); + + // this is the amount we were planning to decommit + ptrdiff_t decommit_size = heap_segment_committed (ephemeral_heap_segment) - decommit_target; + + // we do a max of 160 kB per millisecond (160 MB per second) of pause time + // we don't want to take large pause times too seriously - limit to 10 seconds + ptrdiff_t max_decommit_size = min(ephemeral_elapsed, 10*1000) * 160 * 1024; + decommit_size = min(decommit_size, max_decommit_size); + + slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment) - decommit_size; decommit_heap_segment_pages (ephemeral_heap_segment, slack_space); #endif // !MULTIPLE_HEAPS @@ -31826,8 +31813,13 @@ void gc_heap::decommit_ephemeral_segment_pages() } #ifdef MULTIPLE_HEAPS +// return true if we actually decommitted anything bool gc_heap::decommit_step () { + // should never get here for large pages because decommit_ephemeral_segment_pages + // will not do anything if use_large_pages_p is true + assert(!use_large_pages_p); + size_t decommit_size = 0; for (int i = 0; i < n_heaps; i++) { @@ -31837,6 +31829,7 @@ bool gc_heap::decommit_step () return (decommit_size != 0); } +// return the decommitted size size_t gc_heap::decommit_ephemeral_segment_pages_step () { // we rely on desired allocation not being changed outside of GC diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index 698ac9523e52cf..e4b098535e77af 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -4702,10 +4702,10 @@ class heap_segment uint8_t* background_allocated; #ifdef MULTIPLE_HEAPS gc_heap* heap; - uint8_t* decommit_target; uint8_t* saved_committed; size_t saved_desired_allocation; #endif //MULTIPLE_HEAPS + uint8_t* decommit_target; uint8_t* plan_allocated; uint8_t* saved_bg_allocated; @@ -4741,13 +4741,11 @@ uint8_t*& heap_segment_committed (heap_segment* inst) { return inst->committed; } -#ifdef MULTIPLE_HEAPS inline uint8_t*& heap_segment_decommit_target (heap_segment* inst) { return inst->decommit_target; } -#endif //MULTIPLE_HEAPS inline uint8_t*& heap_segment_used (heap_segment* inst) { From 784dacee62859fa2401b6ceefa9b8efcda7dedb2 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Thu, 7 May 2020 10:24:47 +0200 Subject: [PATCH 06/12] Fix 32-bit compile error --- src/coreclr/src/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 65b603ca78c4ba..40e770fd6b0426 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -31765,7 +31765,7 @@ void gc_heap::decommit_ephemeral_segment_pages() #ifdef FEATURE_CORECLR desired_allocation; #else - dd_max_size (dd); + dd_max_size (dd0); #endif //FEATURE_CORECLR #endif // HOST_64BIT From 40f83cdaad32db7a895aef53dd25ecaaa7e05a76 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Fri, 8 May 2020 16:26:28 +0200 Subject: [PATCH 07/12] Partially address code review feedback. --- src/coreclr/src/gc/gc.cpp | 36 +++++++++++++++++++++--------------- src/coreclr/src/gc/gcpriv.h | 3 --- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 40e770fd6b0426..716ffa2a1b0c83 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -1866,7 +1866,11 @@ const int max_snoop_level = 128; #define MH_TH_CARD_BUNDLE (180*1024*1024) #endif //CARD_BUNDLE -#define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000 +// max size to decommit per millisecond +#define DECOMMIT_SIZE_PER_MILLISECOND (160*1024) + +// time in milliseconds between decommit steps +#define DECOMMIT_TIME_STEP_MILLISECONDS (100) inline size_t align_on_page (size_t add) @@ -2139,8 +2143,6 @@ gc_history_global gc_heap::gc_data_global; size_t gc_heap::gc_last_ephemeral_decommit_time = 0; -size_t gc_heap::gc_gen0_desired_high; - CLRCriticalSection gc_heap::check_commit_cs; size_t gc_heap::current_total_committed = 0; @@ -5396,8 +5398,7 @@ void gc_heap::gc_thread_function () if (heap_number == 0) { - const int DECOMMIT_TIME_STEP = 100; // milliseconds - uint32_t wait_result = gc_heap::ee_suspend_event.Wait(gradual_decommit_in_progress_p ? DECOMMIT_TIME_STEP : INFINITE, FALSE); + uint32_t wait_result = gc_heap::ee_suspend_event.Wait(gradual_decommit_in_progress_p ? DECOMMIT_TIME_STEP_MILLISECONDS : INFINITE, FALSE); if (wait_result == WAIT_TIMEOUT) { gradual_decommit_in_progress_p = decommit_step (); @@ -31787,7 +31788,7 @@ void gc_heap::decommit_ephemeral_segment_pages() gradual_decommit_in_progress_p = TRUE; } // these are only for checking against logic errors - ephemeral_heap_segment->saved_committed = heap_segment_committed(ephemeral_heap_segment); + ephemeral_heap_segment->saved_committed = heap_segment_committed (ephemeral_heap_segment); ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation(dd0); #endif // MULTIPLE_HEAPS @@ -31800,10 +31801,10 @@ void gc_heap::decommit_ephemeral_segment_pages() // this is the amount we were planning to decommit ptrdiff_t decommit_size = heap_segment_committed (ephemeral_heap_segment) - decommit_target; - // we do a max of 160 kB per millisecond (160 MB per second) of pause time - // we don't want to take large pause times too seriously - limit to 10 seconds - ptrdiff_t max_decommit_size = min(ephemeral_elapsed, 10*1000) * 160 * 1024; - decommit_size = min(decommit_size, max_decommit_size); + // we do a max of DECOMMIT_SIZE_PER_MILLISECOND per millisecond of elapsed time since the last GC + // we limit the elapsed time to 10 seconds to avoid spending too much time decommitting + ptrdiff_t max_decommit_size = min (ephemeral_elapsed, (10*1000)) * DECOMMIT_SIZE_PER_MILLISECOND; + decommit_size = min (decommit_size, max_decommit_size); slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment) - decommit_size; decommit_heap_segment_pages (ephemeral_heap_segment, slack_space); @@ -31850,11 +31851,16 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () GCToOSInterface::DebugBreak(); } - const size_t DECOMMIT_STEP_SIZE = max (4096 / n_heaps, 100) * OS_PAGE_SIZE; - uint8_t* page_start = align_on_page ( max (decommit_target, committed - DECOMMIT_STEP_SIZE) ); - size_t size = committed - page_start; - virtual_decommit(page_start, size, heap_number); - dprintf(3, ("Decommitting heap segment [%Ix, %Ix[(%d)", + // limit the decommit size to some reasonable value per time interval + size_t decommit_size = align_on_page ((DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS) / n_heaps); + + // but do at least 100 pages to make the OS call worthwhile + decommit_size = max (decommit_size, 100 * OS_PAGE_SIZE); + + uint8_t* page_start = align_on_page ( max (decommit_target, (committed - decommit_size))); + size_t size = (committed - page_start); + virtual_decommit (page_start, size, heap_number); + dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", (size_t)page_start, (size_t)(page_start + size), size)); diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index e4b098535e77af..965314f1726557 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -3257,9 +3257,6 @@ class gc_heap PER_HEAP_ISOLATED size_t gc_last_ephemeral_decommit_time; - PER_HEAP_ISOLATED - size_t gc_gen0_desired_high; - PER_HEAP size_t gen0_big_free_spaces; From c497aa3f8b8b86251eaf4bad6d126fae55390471 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Thu, 14 May 2020 17:59:48 +0200 Subject: [PATCH 08/12] Address code review feedback, avoid possible overflow in address computation. --- src/coreclr/src/gc/gc.cpp | 57 +++++++++++++++++++++---------------- src/coreclr/src/gc/gcpriv.h | 4 +++ 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 716ffa2a1b0c83..8679b21cfadea8 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -9259,8 +9259,18 @@ void gc_heap::decommit_heap_segment_pages (heap_segment* seg, if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE)) { page_start += max(extra_space, 32*OS_PAGE_SIZE); - size -= max (extra_space, 32*OS_PAGE_SIZE); + decommit_heap_segment_pages_worker (seg, page_start); + } +} +size_t gc_heap::decommit_heap_segment_pages_worker (heap_segment* seg, + uint8_t* new_committed) +{ + assert(!use_large_pages_p); + uint8_t* page_start = align_on_page (new_committed); + size_t size = heap_segment_committed (seg) - page_start; + if (size > 0) + { virtual_decommit (page_start, size, heap_number); dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", (size_t)page_start, @@ -9272,6 +9282,7 @@ void gc_heap::decommit_heap_segment_pages (heap_segment* seg, heap_segment_used (seg) = heap_segment_committed (seg); } } + return size; } //decommit all pages except one or 2 @@ -31787,9 +31798,11 @@ void gc_heap::decommit_ephemeral_segment_pages() { gradual_decommit_in_progress_p = TRUE; } +#ifdef _DEBUG // these are only for checking against logic errors ephemeral_heap_segment->saved_committed = heap_segment_committed (ephemeral_heap_segment); - ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation(dd0); + ephemeral_heap_segment->saved_desired_allocation = dd_desired_allocation (dd0); +#endif // _DEBUG #endif // MULTIPLE_HEAPS #ifndef MULTIPLE_HEAPS @@ -31835,10 +31848,8 @@ bool gc_heap::decommit_step () size_t gc_heap::decommit_ephemeral_segment_pages_step () { // we rely on desired allocation not being changed outside of GC - if (ephemeral_heap_segment->saved_desired_allocation != dd_desired_allocation(dynamic_data_of(0))) - { - GCToOSInterface::DebugBreak(); - } + assert (ephemeral_heap_segment->saved_desired_allocation == dd_desired_allocation (dynamic_data_of(0))); + uint8_t* decommit_target = heap_segment_decommit_target (ephemeral_heap_segment); size_t EXTRA_SPACE = 4 * OS_PAGE_SIZE; decommit_target += EXTRA_SPACE; @@ -31846,30 +31857,26 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () if (decommit_target < committed) { // we rely on other threads not messing with committed if we are about to trim it down - if (ephemeral_heap_segment->saved_committed != heap_segment_committed (ephemeral_heap_segment)) - { - GCToOSInterface::DebugBreak(); - } + assert (ephemeral_heap_segment->saved_committed == heap_segment_committed (ephemeral_heap_segment)); // limit the decommit size to some reasonable value per time interval - size_t decommit_size = align_on_page ((DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS) / n_heaps); + ptrdiff_t decommit_size = ((DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS) / n_heaps); // but do at least 100 pages to make the OS call worthwhile - decommit_size = max (decommit_size, 100 * OS_PAGE_SIZE); + decommit_size = max (decommit_size, (ptrdiff_t)(100 * OS_PAGE_SIZE)); + + // but don't try to do more than what's actually committed in the segment + decommit_size = min ((heap_segment_committed (ephemeral_heap_segment) - heap_segment_mem (ephemeral_heap_segment)), + decommit_size); + + // figure out where the new committed should be + uint8_t* new_committed = max (decommit_target, (committed - decommit_size)); + size_t size = decommit_heap_segment_pages_worker (ephemeral_heap_segment, new_committed); + +#ifdef _DEBUG + ephemeral_heap_segment->saved_committed = committed - size; +#endif // _DEBUG - uint8_t* page_start = align_on_page ( max (decommit_target, (committed - decommit_size))); - size_t size = (committed - page_start); - virtual_decommit (page_start, size, heap_number); - dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", - (size_t)page_start, - (size_t)(page_start + size), - size)); - heap_segment_committed (ephemeral_heap_segment) = page_start; - ephemeral_heap_segment->saved_committed = page_start; - if (heap_segment_used (ephemeral_heap_segment) > heap_segment_committed (ephemeral_heap_segment)) - { - heap_segment_used (ephemeral_heap_segment) = heap_segment_committed (ephemeral_heap_segment); - } return size; } return 0; diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index 965314f1726557..9dc4ad684d3036 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -1652,6 +1652,8 @@ class gc_heap void decommit_heap_segment_pages (heap_segment* seg, size_t extra_space); PER_HEAP size_t decommit_ephemeral_segment_pages_step (); + PER_HEAP + size_t decommit_heap_segment_pages_worker (heap_segment* seg, uint8_t *new_committed); PER_HEAP_ISOLATED bool decommit_step (); PER_HEAP @@ -4699,8 +4701,10 @@ class heap_segment uint8_t* background_allocated; #ifdef MULTIPLE_HEAPS gc_heap* heap; +#ifdef _DEBUG uint8_t* saved_committed; size_t saved_desired_allocation; +#endif // _DEBUG #endif //MULTIPLE_HEAPS uint8_t* decommit_target; uint8_t* plan_allocated; From 83301e4cd22073aaf879b9ea1291974b97070676 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Fri, 15 May 2020 10:41:57 +0200 Subject: [PATCH 09/12] Add spaces in a couple places before opening parentheses. --- src/coreclr/src/gc/gc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 8679b21cfadea8..8f78c4499083e1 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -9266,7 +9266,7 @@ void gc_heap::decommit_heap_segment_pages (heap_segment* seg, size_t gc_heap::decommit_heap_segment_pages_worker (heap_segment* seg, uint8_t* new_committed) { - assert(!use_large_pages_p); + assert (!use_large_pages_p); uint8_t* page_start = align_on_page (new_committed); size_t size = heap_segment_committed (seg) - page_start; if (size > 0) @@ -31833,7 +31833,7 @@ bool gc_heap::decommit_step () { // should never get here for large pages because decommit_ephemeral_segment_pages // will not do anything if use_large_pages_p is true - assert(!use_large_pages_p); + assert (!use_large_pages_p); size_t decommit_size = 0; for (int i = 0; i < n_heaps; i++) @@ -31848,7 +31848,7 @@ bool gc_heap::decommit_step () size_t gc_heap::decommit_ephemeral_segment_pages_step () { // we rely on desired allocation not being changed outside of GC - assert (ephemeral_heap_segment->saved_desired_allocation == dd_desired_allocation (dynamic_data_of(0))); + assert (ephemeral_heap_segment->saved_desired_allocation == dd_desired_allocation (dynamic_data_of (0))); uint8_t* decommit_target = heap_segment_decommit_target (ephemeral_heap_segment); size_t EXTRA_SPACE = 4 * OS_PAGE_SIZE; From e823c421aec2524b2ebddd2615c35cc1a7be1d0d Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Wed, 20 May 2020 15:26:06 +0200 Subject: [PATCH 10/12] Addressed code review feedback about the computation of decommit_size in decommit_ephemeral_pages_step. --- src/coreclr/src/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 8f78c4499083e1..d4a05daab2dc9a 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -31866,7 +31866,7 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () decommit_size = max (decommit_size, (ptrdiff_t)(100 * OS_PAGE_SIZE)); // but don't try to do more than what's actually committed in the segment - decommit_size = min ((heap_segment_committed (ephemeral_heap_segment) - heap_segment_mem (ephemeral_heap_segment)), + decommit_size = min ((heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)), decommit_size); // figure out where the new committed should be From d34eb5b579b73aa3b7a182b12398616728bcadfd Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Mon, 25 May 2020 14:40:00 +0200 Subject: [PATCH 11/12] Fixes to decommit changes: - move computation of reasonable decommit step size to init code - change value of EXTRA_SPACE in decommit_ephemeral_segment_pages_step to be consistent with decommit_heap_segment_pages - reformulate computation of new_committed in decommit_ephemeral_segment_pages_step to eliminate possibility of arithmetic overflow. --- src/coreclr/src/gc/gc.cpp | 27 ++++++++++++++++----------- src/coreclr/src/gc/gcpriv.h | 3 +++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index d4a05daab2dc9a..8f0ffb554eb58f 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -2116,7 +2116,8 @@ int* gc_heap::g_mark_stack_busy; size_t* gc_heap::g_bpromoted; #endif //BACKGROUND_GC -BOOL gc_heap::gradual_decommit_in_progress_p = FALSE; +BOOL gc_heap::gradual_decommit_in_progress_p = FALSE; +size_t gc_heap::max_decommit_step_size = 0; #else //MULTIPLE_HEAPS size_t gc_heap::g_promoted; @@ -10051,6 +10052,14 @@ gc_heap::init_semi_shared() } #endif //MARK_LIST +#ifdef MULTIPLE_HEAPS + // gradual decommit: set size to some reasonable value per time interval + max_decommit_step_size = ((DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS) / n_heaps); + + // but do at least 100 pages per step to make the OS call worthwhile + max_decommit_step_size = max (max_decommit_step_size, (100 * OS_PAGE_SIZE)); +#endif //MULTIPLE_HEAPS + #ifdef FEATURE_BASICFREEZE seg_table = sorted_table::make_sorted_table(); @@ -31851,7 +31860,7 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () assert (ephemeral_heap_segment->saved_desired_allocation == dd_desired_allocation (dynamic_data_of (0))); uint8_t* decommit_target = heap_segment_decommit_target (ephemeral_heap_segment); - size_t EXTRA_SPACE = 4 * OS_PAGE_SIZE; + size_t EXTRA_SPACE = 2 * OS_PAGE_SIZE; decommit_target += EXTRA_SPACE; uint8_t* committed = heap_segment_committed (ephemeral_heap_segment); if (decommit_target < committed) @@ -31859,18 +31868,14 @@ size_t gc_heap::decommit_ephemeral_segment_pages_step () // we rely on other threads not messing with committed if we are about to trim it down assert (ephemeral_heap_segment->saved_committed == heap_segment_committed (ephemeral_heap_segment)); - // limit the decommit size to some reasonable value per time interval - ptrdiff_t decommit_size = ((DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS) / n_heaps); - - // but do at least 100 pages to make the OS call worthwhile - decommit_size = max (decommit_size, (ptrdiff_t)(100 * OS_PAGE_SIZE)); + // how much would we need to decommit to get to decommit_target in one step? + size_t full_decommit_size = (committed - decommit_target); - // but don't try to do more than what's actually committed in the segment - decommit_size = min ((heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)), - decommit_size); + // don't do more than max_decommit_step_size per step + size_t decommit_size = min (max_decommit_step_size, full_decommit_size); // figure out where the new committed should be - uint8_t* new_committed = max (decommit_target, (committed - decommit_size)); + uint8_t* new_committed = (committed - decommit_size); size_t size = decommit_heap_segment_pages_worker (ephemeral_heap_segment, new_committed); #ifdef _DEBUG diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index 9dc4ad684d3036..f6a19543774fd9 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -3786,6 +3786,9 @@ class gc_heap #ifdef MULTIPLE_HEAPS PER_HEAP_ISOLATED BOOL gradual_decommit_in_progress_p; + + PER_HEAP_ISOLATED + size_t max_decommit_step_size; #endif //MULTIPLE_HEAPS #define youngest_generation (generation_of (0)) From 4cd7a10694f38d84cda18c9dfa831b26320901f3 Mon Sep 17 00:00:00 2001 From: Peter Sollich Date: Tue, 26 May 2020 09:44:52 +0200 Subject: [PATCH 12/12] Create a #define MIN_DECOMMIT_SIZE for the 100*OS_PAGE minimum decommit we do per OS call. --- src/coreclr/src/gc/gc.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 8f0ffb554eb58f..db579fab6d3034 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -1866,6 +1866,9 @@ const int max_snoop_level = 128; #define MH_TH_CARD_BUNDLE (180*1024*1024) #endif //CARD_BUNDLE +// min size to decommit to make the OS call worthwhile +#define MIN_DECOMMIT_SIZE (100*OS_PAGE_SIZE) + // max size to decommit per millisecond #define DECOMMIT_SIZE_PER_MILLISECOND (160*1024) @@ -9257,7 +9260,7 @@ void gc_heap::decommit_heap_segment_pages (heap_segment* seg, uint8_t* page_start = align_on_page (heap_segment_allocated(seg)); size_t size = heap_segment_committed (seg) - page_start; extra_space = align_on_page (extra_space); - if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE)) + if (size >= max ((extra_space + 2*OS_PAGE_SIZE), MIN_DECOMMIT_SIZE)) { page_start += max(extra_space, 32*OS_PAGE_SIZE); decommit_heap_segment_pages_worker (seg, page_start); @@ -10056,8 +10059,8 @@ gc_heap::init_semi_shared() // gradual decommit: set size to some reasonable value per time interval max_decommit_step_size = ((DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS) / n_heaps); - // but do at least 100 pages per step to make the OS call worthwhile - max_decommit_step_size = max (max_decommit_step_size, (100 * OS_PAGE_SIZE)); + // but do at least MIN_DECOMMIT_SIZE per step to make the OS call worthwhile + max_decommit_step_size = max (max_decommit_step_size, MIN_DECOMMIT_SIZE); #endif //MULTIPLE_HEAPS #ifdef FEATURE_BASICFREEZE