Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ struct gc_generation {
int threshold; /* collection threshold */
int count; /* count of allocations or collections of younger
generations */
float target; /* target number of collected objects */
int min_threshold; /* minimum threshold */
int max_threshold; /* maximum threshold */
};

/* Running stats per generation */
Expand Down
6 changes: 3 additions & 3 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ extern "C" {
.enabled = 1, \
.generations = { \
/* .head is set in _PyGC_InitState(). */ \
{ .threshold = 700, }, \
{ .threshold = 10, }, \
{ .threshold = 10, }, \
{ .threshold = 700, .target = 0.2, .min_threshold=700, .max_threshold=5000 }, \
{ .threshold = 10, .target = 0.4, .min_threshold=10, .max_threshold=10000 }, \
{ .threshold = 10, .target = 0.6, .min_threshold=10, .max_threshold=50000 }, \
}, \
}, \
.static_objects = { \
Expand Down
41 changes: 41 additions & 0 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,43 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
gc_list_merge(resurrected, old_generation);
}

static void
gc_adapt_threshold(GCState *gcstate, int generation, Py_ssize_t generation_size, Py_ssize_t collected) {
Py_ssize_t threshold = gcstate->generations[generation].threshold;
float target = gcstate->generations[generation].target;

Py_ssize_t new_threshold;

// Calculate the current ratio of collected objects to generation size
double ratio = (double)collected / (double)generation_size;

// Use a formula to adjust the threshold based on the difference between
// the current ratio and the target, and smoothly saturate towards the
// maximum value of MIN/MAX_THRESHOLD using a sigmoid function that grows
// saturates around 0 and 1;

// If the ratio is greater than the target, reduce the threshold
if (ratio > target) {
Py_ssize_t min_threshold = gcstate->generations[generation].min_threshold;
double exponent = -((ratio - target)*10.0-5);
double exxp = 1.0 / (1.0 + exp(exponent));
new_threshold = threshold + (min_threshold - threshold) * exxp;
}
// If the ratio is smaller than the target, increase the threshold
else if (ratio < target) {
Py_ssize_t max_threshold = gcstate->generations[generation].max_threshold;
double exponent = -((target - ratio)*10.0-5);
double exxp = 1.0 / (1.0 + exp(exponent));
new_threshold = threshold + (max_threshold - threshold) * exxp;
}
// If the ratio is equal to the target, do not change the threshold
else {
new_threshold = threshold;
}

gcstate->generations[generation].threshold = new_threshold;
}

/* This is the main function. Read this to understand how the
* collection process works. */
static Py_ssize_t
Expand Down Expand Up @@ -1226,6 +1263,8 @@ gc_collect_main(PyThreadState *tstate, int generation,
old = young;
validate_list(old, collecting_clear_unreachable_clear);

Py_ssize_t gen_size = gc_list_size(young);

deduce_unreachable(young, &unreachable);

untrack_tuples(young);
Expand Down Expand Up @@ -1325,6 +1364,8 @@ gc_collect_main(PyThreadState *tstate, int generation,
}
}

gc_adapt_threshold(gcstate, generation, gen_size, m);

/* Update stats */
if (n_collected) {
*n_collected = m;
Expand Down