diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index b3abe2030a03da..49a3d50ee6e529 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -187,6 +187,11 @@ struct _gc_runtime_state { collections, and are awaiting to undergo a full collection for the first time. */ Py_ssize_t long_lived_pending; + + clock_t last_call_time; + size_t num_calls; + double running_average_time; + double running_average_since_last_call_time; }; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index cb3fce3732c79b..2548e22d66ce6e 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -119,6 +119,10 @@ extern "C" { }, \ .gc = { \ .enabled = 1, \ + .last_call_time = 0, \ + .num_calls = 0, \ + .running_average_time = 0, \ + .running_average_since_last_call_time = 0, \ .generations = { \ /* .head is set in _PyGC_InitState(). */ \ { .threshold = 700, }, \ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 6630faa6f4471d..51ee149afce50c 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2290,8 +2290,25 @@ void _Py_RunGC(PyThreadState *tstate) { GCState *gcstate = &tstate->interp->gc; + + clock_t start_time = clock(); // record start time + size_t num_calls = gcstate->num_calls++; + double elapsed_time_since_last_call = (double)(start_time - gcstate->last_call_time) / CLOCKS_PER_SEC; // calculate elapsed time since last call in seconds + gcstate->running_average_since_last_call_time = (gcstate->running_average_since_last_call_time * num_calls + elapsed_time_since_last_call) / (num_calls + 1); + + if (gcstate->running_average_time / gcstate->running_average_since_last_call_time * 100 > 1.0) { + // If the fraction of gc time over runtime is greater than 1%, don't run gc + return; + } + gcstate->collecting = 1; gc_collect_generations(tstate); + clock_t end_time = clock(); // record end time + + double ellapsed_time = (double)(end_time - start_time) / CLOCKS_PER_SEC; // calculate elapsed time in seconds + gcstate->last_call_time = start_time; + gcstate->running_average_time = (gcstate->running_average_time * num_calls + ellapsed_time) / (num_calls + 1); + gcstate->collecting = 0; } diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 83f4e91e54573a..8f7b96d10772d9 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -22,7 +22,7 @@ at every turn of the eval loop. That variable is set after a wait of `interval` microseconds on `gil_cond` has timed out. - [Actually, another volatile boolean variable (eval_breaker) is used + [Actually, another volatile boolean variablcre (eval_breaker) is used which ORs several conditions into one. Volatile booleans are sufficient as inter-thread signalling means since Python is run on cache-coherent architectures only.]