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

@ -165,7 +165,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
// Functions to clear types free lists
extern void _PyFrame_ClearFreeList(void);
extern void _PyFrame_ClearFreeList(PyThreadState *tstate);
extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
extern void _PyList_ClearFreeList(void);

View File

@ -92,6 +92,12 @@ struct _Py_float_state {
PyFloatObject *free_list;
};
struct _Py_frame_state {
PyFrameObject *free_list;
/* number of frames currently in free_list */
int numfree;
};
/* interpreter state */
@ -187,6 +193,7 @@ struct _is {
#endif
struct _Py_tuple_state tuple;
struct _Py_float_state float_state;
struct _Py_frame_state frame;
/* Using a cache is very effective since typically only a single slice is
created and then deleted again. */

View File

@ -58,7 +58,7 @@ extern PyStatus _PyGC_Init(PyThreadState *tstate);
/* Various internal finalizers */
extern void _PyFrame_Fini(void);
extern void _PyFrame_Fini(PyThreadState *tstate);
extern void _PyDict_Fini(void);
extern void _PyTuple_Fini(PyThreadState *tstate);
extern void _PyList_Fini(void);

View File

@ -1,3 +1,3 @@
The tuple free lists, the empty tuple singleton, the float free list, and the
slice cache are no longer shared by all interpreters: each interpreter now has
its own free lists and caches.
The tuple free lists, the empty tuple singleton, the float free list, the slice
cache, and the frame free list are no longer shared by all interpreters: each
interpreter now its has own free lists and caches.

View File

@ -1026,7 +1026,7 @@ static void
clear_freelists(void)
{
PyThreadState *tstate = _PyThreadState_GET();
_PyFrame_ClearFreeList();
_PyFrame_ClearFreeList(tstate);
_PyTuple_ClearFreeList(tstate);
_PyFloat_ClearFreeList(tstate);
_PyList_ClearFreeList();

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));
}

View File

@ -1249,10 +1249,7 @@ flush_std_files(void)
static void
finalize_interp_types(PyThreadState *tstate, int is_main_interp)
{
if (is_main_interp) {
/* Sundry finalizers */
_PyFrame_Fini();
}
_PyFrame_Fini(tstate);
_PyTuple_Fini(tstate);
if (is_main_interp) {
_PyList_Fini();