bpo-40521: Make frame free list per-interpreter (GH-20638)

Each interpreter now has its own frame free list:

* Move frame free list into PyInterpreterState.
* Add _Py_frame_state structure.
* Add tstate parameter to _PyFrame_ClearFreeList()
  and _PyFrame_Fini().
* Remove "#if PyFrame_MAXFREELIST > 0".
* Remove "#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS".
This commit is contained in:
Victor Stinner 2020-06-05 01:39:24 +02:00 committed by GitHub
parent 7daba6f221
commit 3744ed2c9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 59 deletions

View file

@ -561,36 +561,25 @@ static PyGetSetDef frame_getsetlist[] = {
/* max value for numfree */
#define PyFrame_MAXFREELIST 200
/* bpo-40521: frame free lists are shared by all interpreters. */
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
# undef PyFrame_MAXFREELIST
# define PyFrame_MAXFREELIST 0
#endif
#if PyFrame_MAXFREELIST > 0
static PyFrameObject *free_list = NULL;
static int numfree = 0; /* number of frames currently in free_list */
#endif
static void _Py_HOT_FUNCTION
frame_dealloc(PyFrameObject *f)
{
PyObject **p, **valuestack;
PyCodeObject *co;
if (_PyObject_GC_IS_TRACKED(f))
if (_PyObject_GC_IS_TRACKED(f)) {
_PyObject_GC_UNTRACK(f);
}
Py_TRASHCAN_SAFE_BEGIN(f)
/* Kill all local variables */
valuestack = f->f_valuestack;
for (p = f->f_localsplus; p < valuestack; p++)
PyObject **valuestack = f->f_valuestack;
for (PyObject **p = f->f_localsplus; p < valuestack; p++) {
Py_CLEAR(*p);
}
/* Free stack */
if (f->f_stacktop != NULL) {
for (p = valuestack; p < f->f_stacktop; p++)
for (PyObject **p = valuestack; p < f->f_stacktop; p++) {
Py_XDECREF(*p);
}
}
Py_XDECREF(f->f_back);
@ -599,19 +588,21 @@ frame_dealloc(PyFrameObject *f)
Py_CLEAR(f->f_locals);
Py_CLEAR(f->f_trace);
co = f->f_code;
PyCodeObject *co = f->f_code;
if (co->co_zombieframe == NULL) {
co->co_zombieframe = f;
}
#if PyFrame_MAXFREELIST > 0
else if (numfree < PyFrame_MAXFREELIST) {
++numfree;
f->f_back = free_list;
free_list = f;
}
#endif
else {
PyObject_GC_Del(f);
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _Py_frame_state *state = &interp->frame;
if (state->numfree < PyFrame_MAXFREELIST) {
++state->numfree;
f->f_back = state->free_list;
state->free_list = f;
}
else {
PyObject_GC_Del(f);
}
}
Py_DECREF(co);
@ -789,21 +780,20 @@ frame_alloc(PyCodeObject *code)
Py_ssize_t ncells = PyTuple_GET_SIZE(code->co_cellvars);
Py_ssize_t nfrees = PyTuple_GET_SIZE(code->co_freevars);
Py_ssize_t extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
#if PyFrame_MAXFREELIST > 0
if (free_list == NULL)
#endif
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _Py_frame_state *state = &interp->frame;
if (state->free_list == NULL)
{
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
if (f == NULL) {
return NULL;
}
}
#if PyFrame_MAXFREELIST > 0
else {
assert(numfree > 0);
--numfree;
f = free_list;
free_list = free_list->f_back;
assert(state->numfree > 0);
--state->numfree;
f = state->free_list;
state->free_list = state->free_list->f_back;
if (Py_SIZE(f) < extras) {
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
if (new_f == NULL) {
@ -814,7 +804,6 @@ frame_alloc(PyCodeObject *code)
}
_Py_NewReference((PyObject *)f);
}
#endif
f->f_code = code;
extras = code->co_nlocals + ncells + nfrees;
@ -1183,34 +1172,33 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
/* Clear out the free list */
void
_PyFrame_ClearFreeList(void)
_PyFrame_ClearFreeList(PyThreadState *tstate)
{
#if PyFrame_MAXFREELIST > 0
while (free_list != NULL) {
PyFrameObject *f = free_list;
free_list = free_list->f_back;
struct _Py_frame_state *state = &tstate->interp->frame;
while (state->free_list != NULL) {
PyFrameObject *f = state->free_list;
state->free_list = state->free_list->f_back;
PyObject_GC_Del(f);
--numfree;
--state->numfree;
}
assert(numfree == 0);
#endif
assert(state->numfree == 0);
}
void
_PyFrame_Fini(void)
_PyFrame_Fini(PyThreadState *tstate)
{
_PyFrame_ClearFreeList();
_PyFrame_ClearFreeList(tstate);
}
/* Print summary info about the state of the optimized allocator */
void
_PyFrame_DebugMallocStats(FILE *out)
{
#if PyFrame_MAXFREELIST > 0
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _Py_frame_state *state = &interp->frame;
_PyDebugAllocatorStats(out,
"free PyFrameObject",
numfree, sizeof(PyFrameObject));
#endif
state->numfree, sizeof(PyFrameObject));
}