From 3ff386000379c5970a6b44c1c95ca694efe31eb0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2020 00:36:23 +0200 Subject: [PATCH] bpo-40521: Make frame free list per-interpreter 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 | 2 +- Include/internal/pycore_interp.h | 7 ++ Include/internal/pycore_pylifecycle.h | 2 +- .../2020-05-20-01-17-34.bpo-40521.wvAehI.rst | 6 +- Modules/gcmodule.c | 2 +- Objects/frameobject.c | 86 ++++++++----------- Python/pylifecycle.c | 5 +- 7 files changed, 51 insertions(+), 59 deletions(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index f90d80be16878c..01265d3f985b93 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 70054efe7ec718..9b805f004eaa6e 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 bba9bd9b2bdb22..06d2ac167d6196 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 74c7a499bdef0b..71a1064ba7d143 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 0bad0f8917f372..45dc89d08c1fbb 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 b6d073bd456d03..0fe9f2a6666b24 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 ee9d698d7d0890..1dbdbfdf5a3180 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(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy