Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GC] Handle growing global_regions_to_decommit lists #106168

Closed
wants to merge 16 commits into from
63 changes: 55 additions & 8 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1988,6 +1988,9 @@ const int max_snoop_level = 128;
// time in milliseconds between decommit steps
#define DECOMMIT_TIME_STEP_MILLISECONDS (100)

// arbitrary - 10 GCs worth of decommitting
#define MAX_DECOMMIT_SIZE (10 * DECOMMIT_SIZE_PER_MILLISECOND * DECOMMIT_TIME_STEP_MILLISECONDS)

inline
size_t align_on_page (size_t add)
{
Expand Down Expand Up @@ -2807,6 +2810,7 @@ uint64_t gc_heap::total_uoh_a_last_bgc = 0;

#ifdef USE_REGIONS
region_free_list gc_heap::global_regions_to_decommit[count_free_region_kinds];
size_t gc_heap::to_decommit_size_last_distribute = 0;
region_free_list gc_heap::global_free_huge_regions;
#else //USE_REGIONS
size_t gc_heap::eph_gen_starts_size = 0;
Expand Down Expand Up @@ -13264,7 +13268,7 @@ void gc_heap::age_free_regions (const char* msg)

void gc_heap::distribute_free_regions()
{
const int kind_count = large_free_region + 1;
const int kind_count = count_core_free_region_kinds;

#ifdef MULTIPLE_HEAPS
BOOL joined_last_gc_before_oom = FALSE;
Expand Down Expand Up @@ -13299,6 +13303,8 @@ void gc_heap::distribute_free_regions()
while (decommit_step(DECOMMIT_TIME_STEP_MILLISECONDS))
{
}
to_decommit_size_last_distribute = 0;

#ifdef MULTIPLE_HEAPS
for (int i = 0; i < n_heaps; i++)
{
Expand Down Expand Up @@ -13343,10 +13349,12 @@ void gc_heap::distribute_free_regions()
size_t min_heap_budget_in_region_units[MAX_SUPPORTED_CPUS];
size_t region_size[kind_count] = { global_region_allocator.get_region_alignment(), global_region_allocator.get_large_region_alignment() };
region_free_list surplus_regions[kind_count];
size_t to_decommit_size_start_distribute = 0;
for (int kind = basic_free_region; kind < kind_count; kind++)
{
// we may still have regions left on the regions_to_decommit list -
// we may still have regions left on the global_regions_to_decommit list -
// use these to fill the budget as well
to_decommit_size_start_distribute += global_regions_to_decommit[kind].get_size_free_regions();
surplus_regions[kind].transfer_regions (&global_regions_to_decommit[kind]);
}
#ifdef MULTIPLE_HEAPS
Expand Down Expand Up @@ -13433,6 +13441,20 @@ void gc_heap::distribute_free_regions()

dprintf (1, ("moved %2zd regions (%8zd) to decommit based on time", num_decommit_regions_by_time, size_decommit_regions_by_time));

to_decommit_size_start_distribute += global_regions_to_decommit[huge_free_region].get_size_free_regions();

ptrdiff_t decommit_budget = MAX_DECOMMIT_SIZE;
// If not all entries on the global_regions_to_decommit list were cleared since that last distribute, then don't put more
// on that list than were cleared in the last iteration (with some buffer).
if (to_decommit_size_start_distribute > 0)
{
ptrdiff_t decommit_budget_from_history = to_decommit_size_last_distribute - to_decommit_size_start_distribute;
// Add arbitrary buffer
decommit_budget_from_history = (3 * decommit_budget_from_history) / 2;
// Absolute limit on the number of regions to decommit
decommit_budget = min (decommit_budget, decommit_budget_from_history);
}

global_free_huge_regions.transfer_regions (&global_regions_to_decommit[huge_free_region]);

size_t free_space_in_huge_regions = global_free_huge_regions.get_size_free_regions();
Expand Down Expand Up @@ -13476,18 +13498,36 @@ void gc_heap::distribute_free_regions()
#endif
)
{
// ignore young huge regions when determining how much to decommit
num_regions_to_decommit[kind] =
max(static_cast<ptrdiff_t>(0),
(balance - static_cast<ptrdiff_t>(num_young_huge_region_units_to_consider[kind])));

balance -= num_regions_to_decommit[kind];
num_regions_to_decommit[kind] = balance;

dprintf(REGIONS_LOG, ("distributing the %zd %s regions, removing %zd regions",
total_budget_in_region_units[kind],
kind_name[kind],
num_regions_to_decommit[kind]));

// ignore young huge regions when determining how much to decommit
if (num_young_huge_region_units_to_consider[kind] > 0)
{
num_regions_to_decommit[kind] =
max(static_cast<ptrdiff_t>(0),
(num_regions_to_decommit[kind] - static_cast<ptrdiff_t>(num_young_huge_region_units_to_consider[kind])));
dprintf(REGIONS_LOG, ("ignoring %zd young huge region units, removing %zd region units",
num_young_huge_region_units_to_consider[kind],
num_regions_to_decommit[kind]));
}

// also limit to budget
ptrdiff_t decommit_budget_units = decommit_budget / region_size[kind];
if (num_regions_to_decommit[kind] > decommit_budget_units)
{
num_regions_to_decommit[kind] = decommit_budget_units;
dprintf(REGIONS_LOG, ("limiting removal to %zd region units due to budget",
num_regions_to_decommit[kind]));
}

decommit_budget -= num_regions_to_decommit[kind] * region_size[kind];
balance -= num_regions_to_decommit[kind];

if (num_regions_to_decommit[kind] > 0)
{
// remember how many regions we had on the decommit list already due to aging
Expand Down Expand Up @@ -13658,6 +13698,13 @@ void gc_heap::distribute_free_regions()
}
}
#endif //MULTIPLE_HEAPS

// Record the global_regions_to_decommit sizes for the next redistribute
to_decommit_size_last_distribute = 0;
for (int kind = basic_free_region; kind < count_free_region_kinds; kind++)
{
to_decommit_size_last_distribute += global_regions_to_decommit[kind].get_size_free_regions();
}
}
#endif //USE_REGIONS

Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/gc/gcpriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,10 @@ enum free_region_kind
large_free_region,
huge_free_region,
count_free_region_kinds,

// Many calculations consider huge regions to be a number of large region units.
// This count excludes huge regions for those cases.
count_core_free_region_kinds = large_free_region + 1,
};

static_assert(count_free_region_kinds == FREE_REGION_KINDS, "Keep count_free_region_kinds in sync with FREE_REGION_KINDS, changing this is not a version breaking change.");
Expand Down Expand Up @@ -4263,6 +4267,9 @@ class gc_heap
PER_HEAP_ISOLATED_FIELD_MAINTAINED uint8_t*** g_mark_list_piece;

PER_HEAP_ISOLATED_FIELD_MAINTAINED region_free_list global_regions_to_decommit[count_free_region_kinds];
// At the end of each distribute_free_regions call, we record the size of global_regions_to_decommit so that
// we can see the decommit progress during the next call.
PER_HEAP_ISOLATED_FIELD_MAINTAINED size_t to_decommit_size_last_distribute;

PER_HEAP_ISOLATED_FIELD_MAINTAINED region_free_list global_free_huge_regions;
#else //USE_REGIONS
Expand Down
Loading