mirror of
https://github.com/python/cpython.git
synced 2026-01-07 16:02:55 +00:00
GH-139951: Fix major GC performance regression (GH-140262)
* Count number of actually tracked objects, instead of trackable objects. This ensures that untracking tuples has the desired effect of reducing GC overhead * Do not track most untrackable tuples during creation. This prevents large numbers of small tuples causing execessive GCs.
This commit is contained in:
parent
4adf08ae46
commit
0c01090ad9
5 changed files with 93 additions and 36 deletions
43
Python/gc.c
43
Python/gc.c
|
|
@ -1639,7 +1639,7 @@ assess_work_to_do(GCState *gcstate)
|
|||
scale_factor = 2;
|
||||
}
|
||||
intptr_t new_objects = gcstate->young.count;
|
||||
intptr_t max_heap_fraction = new_objects*3/2;
|
||||
intptr_t max_heap_fraction = new_objects*2;
|
||||
intptr_t heap_fraction = gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor;
|
||||
if (heap_fraction > max_heap_fraction) {
|
||||
heap_fraction = max_heap_fraction;
|
||||
|
|
@ -1654,6 +1654,9 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
|
|||
GC_STAT_ADD(1, collections, 1);
|
||||
GCState *gcstate = &tstate->interp->gc;
|
||||
gcstate->work_to_do += assess_work_to_do(gcstate);
|
||||
if (gcstate->work_to_do < 0) {
|
||||
return;
|
||||
}
|
||||
untrack_tuples(&gcstate->young.head);
|
||||
if (gcstate->phase == GC_PHASE_MARK) {
|
||||
Py_ssize_t objects_marked = mark_at_start(tstate);
|
||||
|
|
@ -1696,7 +1699,6 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
|
|||
gc_collect_region(tstate, &increment, &survivors, stats);
|
||||
gc_list_merge(&survivors, visited);
|
||||
assert(gc_list_is_empty(&increment));
|
||||
gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor;
|
||||
gcstate->work_to_do -= increment_size;
|
||||
|
||||
add_stats(gcstate, 1, stats);
|
||||
|
|
@ -2299,21 +2301,11 @@ _Py_ScheduleGC(PyThreadState *tstate)
|
|||
}
|
||||
|
||||
void
|
||||
_PyObject_GC_Link(PyObject *op)
|
||||
_Py_TriggerGC(struct _gc_runtime_state *gcstate)
|
||||
{
|
||||
PyGC_Head *gc = AS_GC(op);
|
||||
// gc must be correctly aligned
|
||||
_PyObject_ASSERT(op, ((uintptr_t)gc & (sizeof(uintptr_t)-1)) == 0);
|
||||
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
GCState *gcstate = &tstate->interp->gc;
|
||||
gc->_gc_next = 0;
|
||||
gc->_gc_prev = 0;
|
||||
gcstate->young.count++; /* number of allocated GC objects */
|
||||
gcstate->heap_size++;
|
||||
if (gcstate->young.count > gcstate->young.threshold &&
|
||||
gcstate->enabled &&
|
||||
gcstate->young.threshold &&
|
||||
if (gcstate->enabled &&
|
||||
gcstate->young.threshold != 0 &&
|
||||
!_Py_atomic_load_int_relaxed(&gcstate->collecting) &&
|
||||
!_PyErr_Occurred(tstate))
|
||||
{
|
||||
|
|
@ -2321,6 +2313,17 @@ _PyObject_GC_Link(PyObject *op)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
_PyObject_GC_Link(PyObject *op)
|
||||
{
|
||||
PyGC_Head *gc = AS_GC(op);
|
||||
// gc must be correctly aligned
|
||||
_PyObject_ASSERT(op, ((uintptr_t)gc & (sizeof(uintptr_t)-1)) == 0);
|
||||
gc->_gc_next = 0;
|
||||
gc->_gc_prev = 0;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
_Py_RunGC(PyThreadState *tstate)
|
||||
{
|
||||
|
|
@ -2427,6 +2430,11 @@ PyObject_GC_Del(void *op)
|
|||
PyGC_Head *g = AS_GC(op);
|
||||
if (_PyObject_GC_IS_TRACKED(op)) {
|
||||
gc_list_remove(g);
|
||||
GCState *gcstate = get_gc_state();
|
||||
if (gcstate->young.count > 0) {
|
||||
gcstate->young.count--;
|
||||
}
|
||||
gcstate->heap_size--;
|
||||
#ifdef Py_DEBUG
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0,
|
||||
|
|
@ -2440,11 +2448,6 @@ PyObject_GC_Del(void *op)
|
|||
PyErr_SetRaisedException(exc);
|
||||
#endif
|
||||
}
|
||||
GCState *gcstate = get_gc_state();
|
||||
if (gcstate->young.count > 0) {
|
||||
gcstate->young.count--;
|
||||
}
|
||||
gcstate->heap_size--;
|
||||
PyObject_Free(((char *)op)-presize);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue