mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
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:
parent
7daba6f221
commit
3744ed2c9c
7 changed files with 51 additions and 59 deletions
|
@ -165,7 +165,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
|
||||||
|
|
||||||
|
|
||||||
// Functions to clear types free lists
|
// 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 _PyTuple_ClearFreeList(PyThreadState *tstate);
|
||||||
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
|
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
|
||||||
extern void _PyList_ClearFreeList(void);
|
extern void _PyList_ClearFreeList(void);
|
||||||
|
|
|
@ -92,6 +92,12 @@ struct _Py_float_state {
|
||||||
PyFloatObject *free_list;
|
PyFloatObject *free_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _Py_frame_state {
|
||||||
|
PyFrameObject *free_list;
|
||||||
|
/* number of frames currently in free_list */
|
||||||
|
int numfree;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* interpreter state */
|
/* interpreter state */
|
||||||
|
|
||||||
|
@ -187,6 +193,7 @@ struct _is {
|
||||||
#endif
|
#endif
|
||||||
struct _Py_tuple_state tuple;
|
struct _Py_tuple_state tuple;
|
||||||
struct _Py_float_state float_state;
|
struct _Py_float_state float_state;
|
||||||
|
struct _Py_frame_state frame;
|
||||||
|
|
||||||
/* Using a cache is very effective since typically only a single slice is
|
/* Using a cache is very effective since typically only a single slice is
|
||||||
created and then deleted again. */
|
created and then deleted again. */
|
||||||
|
|
|
@ -58,7 +58,7 @@ extern PyStatus _PyGC_Init(PyThreadState *tstate);
|
||||||
|
|
||||||
/* Various internal finalizers */
|
/* Various internal finalizers */
|
||||||
|
|
||||||
extern void _PyFrame_Fini(void);
|
extern void _PyFrame_Fini(PyThreadState *tstate);
|
||||||
extern void _PyDict_Fini(void);
|
extern void _PyDict_Fini(void);
|
||||||
extern void _PyTuple_Fini(PyThreadState *tstate);
|
extern void _PyTuple_Fini(PyThreadState *tstate);
|
||||||
extern void _PyList_Fini(void);
|
extern void _PyList_Fini(void);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
The tuple free lists, the empty tuple singleton, the float free list, and the
|
The tuple free lists, the empty tuple singleton, the float free list, the slice
|
||||||
slice cache are no longer shared by all interpreters: each interpreter now has
|
cache, and the frame free list are no longer shared by all interpreters: each
|
||||||
its own free lists and caches.
|
interpreter now its has own free lists and caches.
|
||||||
|
|
|
@ -1026,7 +1026,7 @@ static void
|
||||||
clear_freelists(void)
|
clear_freelists(void)
|
||||||
{
|
{
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
_PyFrame_ClearFreeList();
|
_PyFrame_ClearFreeList(tstate);
|
||||||
_PyTuple_ClearFreeList(tstate);
|
_PyTuple_ClearFreeList(tstate);
|
||||||
_PyFloat_ClearFreeList(tstate);
|
_PyFloat_ClearFreeList(tstate);
|
||||||
_PyList_ClearFreeList();
|
_PyList_ClearFreeList();
|
||||||
|
|
|
@ -561,36 +561,25 @@ static PyGetSetDef frame_getsetlist[] = {
|
||||||
/* max value for numfree */
|
/* max value for numfree */
|
||||||
#define PyFrame_MAXFREELIST 200
|
#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
|
static void _Py_HOT_FUNCTION
|
||||||
frame_dealloc(PyFrameObject *f)
|
frame_dealloc(PyFrameObject *f)
|
||||||
{
|
{
|
||||||
PyObject **p, **valuestack;
|
if (_PyObject_GC_IS_TRACKED(f)) {
|
||||||
PyCodeObject *co;
|
|
||||||
|
|
||||||
if (_PyObject_GC_IS_TRACKED(f))
|
|
||||||
_PyObject_GC_UNTRACK(f);
|
_PyObject_GC_UNTRACK(f);
|
||||||
|
}
|
||||||
|
|
||||||
Py_TRASHCAN_SAFE_BEGIN(f)
|
Py_TRASHCAN_SAFE_BEGIN(f)
|
||||||
/* Kill all local variables */
|
/* Kill all local variables */
|
||||||
valuestack = f->f_valuestack;
|
PyObject **valuestack = f->f_valuestack;
|
||||||
for (p = f->f_localsplus; p < valuestack; p++)
|
for (PyObject **p = f->f_localsplus; p < valuestack; p++) {
|
||||||
Py_CLEAR(*p);
|
Py_CLEAR(*p);
|
||||||
|
}
|
||||||
|
|
||||||
/* Free stack */
|
/* Free stack */
|
||||||
if (f->f_stacktop != NULL) {
|
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(*p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_XDECREF(f->f_back);
|
Py_XDECREF(f->f_back);
|
||||||
|
@ -599,19 +588,21 @@ frame_dealloc(PyFrameObject *f)
|
||||||
Py_CLEAR(f->f_locals);
|
Py_CLEAR(f->f_locals);
|
||||||
Py_CLEAR(f->f_trace);
|
Py_CLEAR(f->f_trace);
|
||||||
|
|
||||||
co = f->f_code;
|
PyCodeObject *co = f->f_code;
|
||||||
if (co->co_zombieframe == NULL) {
|
if (co->co_zombieframe == NULL) {
|
||||||
co->co_zombieframe = f;
|
co->co_zombieframe = f;
|
||||||
}
|
}
|
||||||
#if PyFrame_MAXFREELIST > 0
|
|
||||||
else if (numfree < PyFrame_MAXFREELIST) {
|
|
||||||
++numfree;
|
|
||||||
f->f_back = free_list;
|
|
||||||
free_list = f;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else {
|
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);
|
Py_DECREF(co);
|
||||||
|
@ -789,21 +780,20 @@ frame_alloc(PyCodeObject *code)
|
||||||
Py_ssize_t ncells = PyTuple_GET_SIZE(code->co_cellvars);
|
Py_ssize_t ncells = PyTuple_GET_SIZE(code->co_cellvars);
|
||||||
Py_ssize_t nfrees = PyTuple_GET_SIZE(code->co_freevars);
|
Py_ssize_t nfrees = PyTuple_GET_SIZE(code->co_freevars);
|
||||||
Py_ssize_t extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
|
Py_ssize_t extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
|
||||||
#if PyFrame_MAXFREELIST > 0
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if (free_list == NULL)
|
struct _Py_frame_state *state = &interp->frame;
|
||||||
#endif
|
if (state->free_list == NULL)
|
||||||
{
|
{
|
||||||
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
|
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if PyFrame_MAXFREELIST > 0
|
|
||||||
else {
|
else {
|
||||||
assert(numfree > 0);
|
assert(state->numfree > 0);
|
||||||
--numfree;
|
--state->numfree;
|
||||||
f = free_list;
|
f = state->free_list;
|
||||||
free_list = free_list->f_back;
|
state->free_list = state->free_list->f_back;
|
||||||
if (Py_SIZE(f) < extras) {
|
if (Py_SIZE(f) < extras) {
|
||||||
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
|
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
|
||||||
if (new_f == NULL) {
|
if (new_f == NULL) {
|
||||||
|
@ -814,7 +804,6 @@ frame_alloc(PyCodeObject *code)
|
||||||
}
|
}
|
||||||
_Py_NewReference((PyObject *)f);
|
_Py_NewReference((PyObject *)f);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
f->f_code = code;
|
f->f_code = code;
|
||||||
extras = code->co_nlocals + ncells + nfrees;
|
extras = code->co_nlocals + ncells + nfrees;
|
||||||
|
@ -1183,34 +1172,33 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
||||||
|
|
||||||
/* Clear out the free list */
|
/* Clear out the free list */
|
||||||
void
|
void
|
||||||
_PyFrame_ClearFreeList(void)
|
_PyFrame_ClearFreeList(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
#if PyFrame_MAXFREELIST > 0
|
struct _Py_frame_state *state = &tstate->interp->frame;
|
||||||
while (free_list != NULL) {
|
while (state->free_list != NULL) {
|
||||||
PyFrameObject *f = free_list;
|
PyFrameObject *f = state->free_list;
|
||||||
free_list = free_list->f_back;
|
state->free_list = state->free_list->f_back;
|
||||||
PyObject_GC_Del(f);
|
PyObject_GC_Del(f);
|
||||||
--numfree;
|
--state->numfree;
|
||||||
}
|
}
|
||||||
assert(numfree == 0);
|
assert(state->numfree == 0);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyFrame_Fini(void)
|
_PyFrame_Fini(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
_PyFrame_ClearFreeList();
|
_PyFrame_ClearFreeList(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print summary info about the state of the optimized allocator */
|
/* Print summary info about the state of the optimized allocator */
|
||||||
void
|
void
|
||||||
_PyFrame_DebugMallocStats(FILE *out)
|
_PyFrame_DebugMallocStats(FILE *out)
|
||||||
{
|
{
|
||||||
#if PyFrame_MAXFREELIST > 0
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
struct _Py_frame_state *state = &interp->frame;
|
||||||
_PyDebugAllocatorStats(out,
|
_PyDebugAllocatorStats(out,
|
||||||
"free PyFrameObject",
|
"free PyFrameObject",
|
||||||
numfree, sizeof(PyFrameObject));
|
state->numfree, sizeof(PyFrameObject));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1249,10 +1249,7 @@ flush_std_files(void)
|
||||||
static void
|
static void
|
||||||
finalize_interp_types(PyThreadState *tstate, int is_main_interp)
|
finalize_interp_types(PyThreadState *tstate, int is_main_interp)
|
||||||
{
|
{
|
||||||
if (is_main_interp) {
|
_PyFrame_Fini(tstate);
|
||||||
/* Sundry finalizers */
|
|
||||||
_PyFrame_Fini();
|
|
||||||
}
|
|
||||||
_PyTuple_Fini(tstate);
|
_PyTuple_Fini(tstate);
|
||||||
if (is_main_interp) {
|
if (is_main_interp) {
|
||||||
_PyList_Fini();
|
_PyList_Fini();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue