aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-06-05 01:39:24 +0200
committerGitHub <noreply@github.com>2020-06-05 01:39:24 +0200
commit3744ed2c9c0b3905947602fc375de49533790cb9 (patch)
treef2086f04a9edd9875c6f0ec2eb1f2f0d6e85aa41
parent7daba6f221e713f7f60c613b246459b07d179f91 (diff)
downloadcpython3-3744ed2c9c0b3905947602fc375de49533790cb9.tar.gz
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".
-rw-r--r--Include/internal/pycore_gc.h2
-rw-r--r--Include/internal/pycore_interp.h7
-rw-r--r--Include/internal/pycore_pylifecycle.h2
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst6
-rw-r--r--Modules/gcmodule.c2
-rw-r--r--Objects/frameobject.c86
-rw-r--r--Python/pylifecycle.c5
7 files changed, 51 insertions, 59 deletions
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index f90d80be16..01265d3f98 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -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);
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 70054efe7e..9b805f004e 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -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. */
diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h
index bba9bd9b2b..06d2ac167d 100644
--- a/Include/internal/pycore_pylifecycle.h
+++ b/Include/internal/pycore_pylifecycle.h
@@ -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);
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst
index 74c7a499bd..71a1064ba7 100644
--- a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst
@@ -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.
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 0bad0f8917..45dc89d08c 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -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();
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index b6d073bd45..0fe9f2a666 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -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));
}
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index ee9d698d7d..1dbdbfdf5a 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -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();