From 4b7acce5eb3a8e6aa648e95f658162f61d761401 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2020 01:00:56 +0200 Subject: [PATCH] bpo-40521: Make list free list per-interpreter Each interpreter now has its own list free list: * Move list numfree and free_list into PyInterpreterState. * Add _Py_list_state structure. * Add tstate parameter to _PyList_ClearFreeList() and _PyList_Fini(). * Remove "#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS". * _PyGC_Fini() clears gcstate->garbage list which can be stored in the list free list. Call _PyGC_Fini() before _PyList_Fini() to prevent leaking this list. --- Include/internal/pycore_gc.h | 2 +- Include/internal/pycore_interp.h | 11 ++++ Include/internal/pycore_pylifecycle.h | 2 +- .../2020-05-20-01-17-34.bpo-40521.wvAehI.rst | 6 +- Modules/gcmodule.c | 2 +- Objects/listobject.c | 60 +++++++++---------- Python/pylifecycle.c | 6 +- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 01265d3f985b93..3388b4d69e2644 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -168,7 +168,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *); extern void _PyFrame_ClearFreeList(PyThreadState *tstate); extern void _PyTuple_ClearFreeList(PyThreadState *tstate); extern void _PyFloat_ClearFreeList(PyThreadState *tstate); -extern void _PyList_ClearFreeList(void); +extern void _PyList_ClearFreeList(PyThreadState *tstate); extern void _PyDict_ClearFreeList(void); extern void _PyAsyncGen_ClearFreeLists(void); extern void _PyContext_ClearFreeList(void); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 9b805f004eaa6e..0eab246562051f 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -84,6 +84,16 @@ struct _Py_tuple_state { #endif }; +/* Empty list reuse scheme to save calls to malloc and free */ +#ifndef PyList_MAXFREELIST +# define PyList_MAXFREELIST 80 +#endif + +struct _Py_list_state { + PyListObject *free_list[PyList_MAXFREELIST]; + int numfree; +}; + struct _Py_float_state { /* Special free list free_list is a singly-linked list of available PyFloatObjects, @@ -192,6 +202,7 @@ struct _is { PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; #endif struct _Py_tuple_state tuple; + struct _Py_list_state list; struct _Py_float_state float_state; struct _Py_frame_state frame; diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 06d2ac167d6196..3c35ca23eab1a8 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -61,7 +61,7 @@ extern PyStatus _PyGC_Init(PyThreadState *tstate); extern void _PyFrame_Fini(PyThreadState *tstate); extern void _PyDict_Fini(void); extern void _PyTuple_Fini(PyThreadState *tstate); -extern void _PyList_Fini(void); +extern void _PyList_Fini(PyThreadState *tstate); extern void _PySet_Fini(void); extern void _PyBytes_Fini(void); extern void _PyFloat_Fini(PyThreadState *tstate); 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 71a1064ba7d143..54cc60036164ef 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, 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. +The tuple free lists, the empty tuple singleton, the list free list, 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 45dc89d08c1fbb..2f062d0022589d 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1029,7 +1029,7 @@ clear_freelists(void) _PyFrame_ClearFreeList(tstate); _PyTuple_ClearFreeList(tstate); _PyFloat_ClearFreeList(tstate); - _PyList_ClearFreeList(); + _PyList_ClearFreeList(tstate); _PyDict_ClearFreeList(); _PyAsyncGen_ClearFreeLists(); _PyContext_ClearFreeList(); diff --git a/Objects/listobject.c b/Objects/listobject.c index 30d26207537443..043256d8adbf5c 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -96,65 +96,59 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size) return 0; } -/* Empty list reuse scheme to save calls to malloc and free */ -#ifndef PyList_MAXFREELIST -# define PyList_MAXFREELIST 80 -#endif - -/* bpo-40521: list free lists are shared by all interpreters. */ -#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS -# undef PyList_MAXFREELIST -# define PyList_MAXFREELIST 0 -#endif - -static PyListObject *free_list[PyList_MAXFREELIST]; -static int numfree = 0; - void -_PyList_ClearFreeList(void) +_PyList_ClearFreeList(PyThreadState *tstate) { - while (numfree) { - PyListObject *op = free_list[--numfree]; + struct _Py_list_state *state = &tstate->interp->list; + while (state->numfree) { + PyListObject *op = state->free_list[--state->numfree]; assert(PyList_CheckExact(op)); PyObject_GC_Del(op); } } void -_PyList_Fini(void) +_PyList_Fini(PyThreadState *tstate) { - _PyList_ClearFreeList(); + _PyList_ClearFreeList(tstate); } /* Print summary info about the state of the optimized allocator */ void _PyList_DebugMallocStats(FILE *out) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_list_state *state = &interp->list; _PyDebugAllocatorStats(out, "free PyListObject", - numfree, sizeof(PyListObject)); + state->numfree, sizeof(PyListObject)); } PyObject * PyList_New(Py_ssize_t size) { - PyListObject *op; - if (size < 0) { PyErr_BadInternalCall(); return NULL; } - if (numfree) { - numfree--; - op = free_list[numfree]; + + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_list_state *state = &interp->list; + PyListObject *op; + if (state->numfree) { + state->numfree--; + op = state->free_list[state->numfree]; _Py_NewReference((PyObject *)op); - } else { + } + else { op = PyObject_GC_New(PyListObject, &PyList_Type); - if (op == NULL) + if (op == NULL) { return NULL; + } } - if (size <= 0) + if (size <= 0) { op->ob_item = NULL; + } else { op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *)); if (op->ob_item == NULL) { @@ -334,10 +328,14 @@ list_dealloc(PyListObject *op) } PyMem_FREE(op->ob_item); } - if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) - free_list[numfree++] = op; - else + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_list_state *state = &interp->list; + if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) { + state->free_list[state->numfree++] = op; + } + else { Py_TYPE(op)->tp_free((PyObject *)op); + } Py_TRASHCAN_END } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 1dbdbfdf5a3180..09d4d884041447 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1251,8 +1251,8 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp) { _PyFrame_Fini(tstate); _PyTuple_Fini(tstate); + _PyList_Fini(tstate); if (is_main_interp) { - _PyList_Fini(); _PySet_Fini(); _PyBytes_Fini(); } @@ -1296,6 +1296,8 @@ finalize_interp_clear(PyThreadState *tstate) _PyGC_CollectNoFail(); } + _PyGC_Fini(tstate); + finalize_interp_types(tstate, is_main_interp); if (is_main_interp) { @@ -1309,8 +1311,6 @@ finalize_interp_clear(PyThreadState *tstate) _PyExc_Fini(); } - - _PyGC_Fini(tstate); } 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