]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40521: Make frame free list per-interpreter (GH-20638)
authorVictor Stinner <vstinner@python.org>
Thu, 4 Jun 2020 23:39:24 +0000 (01:39 +0200)
committerGitHub <noreply@github.com>
Thu, 4 Jun 2020 23:39:24 +0000 (01:39 +0200)
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".

Include/internal/pycore_gc.h
Include/internal/pycore_interp.h
Include/internal/pycore_pylifecycle.h
Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst
Modules/gcmodule.c
Objects/frameobject.c
Python/pylifecycle.c

index f90d80be16878cce2dfd446c78ab08a0d0032e04..01265d3f985b93470f307bc7384cd1cdae92cac8 100644 (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);
index 70054efe7ec718eb9a4e988629a03f0d2dfb7efb..9b805f004eaa6ea8a0f18af5f3f24415206b2781 100644 (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. */
index bba9bd9b2bdb229ff2c92cb8a6011fb66d8f9b9a..06d2ac167d6196ffac0c2b7bba9d0af066b7f367 100644 (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);
index 74c7a499bdef0b749d6ae8a45c155ae895b25cb2..71a1064ba7d143765bdd8138504888041996d9e6 100644 (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.
index 0bad0f8917f3725b8d0321bd76a9d86c2197ffc2..45dc89d08c1fbbf9d8b74b6f78fecf57ddcc05e8 100644 (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();
index b6d073bd456d03ba7630e4e49b1ba9ce62119be9..0fe9f2a6666b24b529ce6f163b224cab8c798ff4 100644 (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));
 }
 
 
index ee9d698d7d0890628aea8cd29e553b3558de721f..1dbdbfdf5a3180b440906c93d17a941577de4e60 100644 (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();