Skip to content

Commit

Permalink
Add a time-based decay to the linear allocation model. (#55174)
Browse files Browse the repository at this point in the history
Real world scenario that motivated this allocated a huge amount of data once a day, leading to high gen 0 and gen 1 budgets (several GB).

After this, there was relatively little activity, causing gen 1 budget to take several hours to normalize.

The new logic discounts the previous desired allocation over a period of 5 minutes.
  • Loading branch information
PeterSolMS committed Jul 16, 2021
1 parent 5519852 commit 0ac3a5b
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 14 deletions.
29 changes: 15 additions & 14 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20265,6 +20265,7 @@ void gc_heap::update_collection_counts ()
}

dd_gc_clock (dd) = dd_gc_clock (dd0);
dd_previous_time_clock (dd) = dd_time_clock (dd);
dd_time_clock (dd) = now;
}
}
Expand Down Expand Up @@ -36712,6 +36713,7 @@ bool gc_heap::init_dynamic_data()
dynamic_data* dd = dynamic_data_of (i);
dd->gc_clock = 0;
dd->time_clock = now;
dd->previous_time_clock = now;
dd->current_size = 0;
dd->promoted_size = 0;
dd->collection_count = 0;
Expand All @@ -36737,21 +36739,19 @@ float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
//not be correct (collection happened too soon). Correct with a linear estimation based on the previous
//value of the budget
static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation,
size_t previous_desired_allocation, size_t collection_count)
size_t previous_desired_allocation, float time_since_previous_collection_secs)
{
if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
{
dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
const float decay_time = 5*60.0f; // previous desired allocation expires over 5 minutes
float decay_factor = (decay_time <= time_since_previous_collection_secs) ?
0 :
((decay_time - time_since_previous_collection_secs) / decay_time);
float previous_allocation_factor = (1.0f - allocation_fraction) * decay_factor;
dprintf (2, ("allocation fraction: %d, decay factor: %d, previous allocation factor: %d",
(int)(allocation_fraction*100.0), (int)(decay_factor*100.0), (int)(previous_allocation_factor*100.0)));
new_allocation = (size_t)((1.0 - previous_allocation_factor)*new_allocation + previous_allocation_factor * previous_desired_allocation);
}
#if 0
size_t smoothing = 3; // exponential smoothing factor
if (smoothing > collection_count)
smoothing = collection_count;
new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
#else
UNREFERENCED_PARAMETER(collection_count);
#endif //0
return new_allocation;
}

Expand All @@ -36778,6 +36778,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd,
float f = 0;
size_t max_size = dd_max_size (dd);
size_t new_allocation = 0;
float time_since_previous_collection_secs = (dd_time_clock (dd) - dd_previous_time_clock (dd))*1e-6f;
float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));

if (gen_number >= max_generation)
Expand All @@ -36804,7 +36805,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd,
new_allocation = max((new_size - current_size), min_gc_size);

new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
dd_desired_allocation (dd), dd_collection_count (dd));
dd_desired_allocation (dd), time_since_previous_collection_secs);

if (
#ifdef BGC_SERVO_TUNING
Expand Down Expand Up @@ -36856,7 +36857,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd,
max ((current_size/4), min_gc_size));

new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
dd_desired_allocation (dd), dd_collection_count (dd));
dd_desired_allocation (dd), time_since_previous_collection_secs);

}
}
Expand All @@ -36868,7 +36869,7 @@ size_t gc_heap::desired_new_allocation (dynamic_data* dd,
new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);

new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
dd_desired_allocation (dd), dd_collection_count (dd));
dd_desired_allocation (dd), time_since_previous_collection_secs);

if (gen_number == 0)
{
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 @@ -885,6 +885,7 @@ class dynamic_data
size_t fragmentation; //fragmentation when we don't compact
size_t gc_clock; //gc# when last GC happened
uint64_t time_clock; //time when last gc started
uint64_t previous_time_clock; // time when previous gc started
size_t gc_elapsed_time; // Time it took for the gc to complete
float gc_speed; // speed in bytes/msec for the gc to complete

Expand Down Expand Up @@ -5004,6 +5005,12 @@ uint64_t& dd_time_clock (dynamic_data* inst)
{
return inst->time_clock;
}
inline
uint64_t& dd_previous_time_clock (dynamic_data* inst)
{
return inst->previous_time_clock;
}


inline
size_t& dd_gc_clock_interval (dynamic_data* inst)
Expand Down

0 comments on commit 0ac3a5b

Please sign in to comment.