From 2ba59370c3dda2ac229c14510e53a05074b133d1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2020 00:50:05 +0200 Subject: bpo-40521: Make float free list per-interpreter (GH-20636) Each interpreter now has its own float free list: * Move tuple numfree and free_list into PyInterpreterState. * Add _Py_float_state structure. * Add tstate parameter to _PyFloat_ClearFreeList() and _PyFloat_Fini(). --- Include/internal/pycore_gc.h | 2 +- Include/internal/pycore_interp.h | 9 ++++ Include/internal/pycore_pylifecycle.h | 2 +- .../2020-05-20-01-17-34.bpo-40521.wvAehI.rst | 3 +- Modules/gcmodule.c | 2 +- Objects/floatobject.c | 55 ++++++++++++---------- Python/pylifecycle.c | 2 +- 7 files changed, 44 insertions(+), 31 deletions(-) diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index e8e5d32..f90d80b 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -167,7 +167,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *); // Functions to clear types free lists extern void _PyFrame_ClearFreeList(void); extern void _PyTuple_ClearFreeList(PyThreadState *tstate); -extern void _PyFloat_ClearFreeList(void); +extern void _PyFloat_ClearFreeList(PyThreadState *tstate); extern void _PyList_ClearFreeList(void); extern void _PyDict_ClearFreeList(void); extern void _PyAsyncGen_ClearFreeLists(void); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index b90bfbe..c0eed00 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -84,6 +84,14 @@ struct _Py_tuple_state { #endif }; +struct _Py_float_state { + /* Special free list + free_list is a singly-linked list of available PyFloatObjects, + linked via abuse of their ob_type members. */ + int numfree; + PyFloatObject *free_list; +}; + /* interpreter state */ @@ -178,6 +186,7 @@ struct _is { PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS]; #endif struct _Py_tuple_state tuple; + struct _Py_float_state float_state; }; /* Used by _PyImport_Cleanup() */ diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 3f2ff5b..2643abc 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -64,7 +64,7 @@ extern void _PyTuple_Fini(PyThreadState *tstate); extern void _PyList_Fini(void); extern void _PySet_Fini(void); extern void _PyBytes_Fini(void); -extern void _PyFloat_Fini(void); +extern void _PyFloat_Fini(PyThreadState *tstate); extern void _PySlice_Fini(void); extern void _PyAsyncGen_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 f364d36..016f116 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 +1,2 @@ -Each interpreter now has its own tuple free lists and empty tuple singleton. +Tuple free lists, empty tuple singleton, and float free list are no longer +shared by all interpreters: each interpreter now its own free lists. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 1f5aa93..0bad0f8 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1028,7 +1028,7 @@ clear_freelists(void) PyThreadState *tstate = _PyThreadState_GET(); _PyFrame_ClearFreeList(); _PyTuple_ClearFreeList(tstate); - _PyFloat_ClearFreeList(); + _PyFloat_ClearFreeList(tstate); _PyList_ClearFreeList(); _PyDict_ClearFreeList(); _PyAsyncGen_ClearFreeLists(); diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 868b729..d72fd21 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -5,6 +5,8 @@ #include "Python.h" #include "pycore_dtoa.h" +#include "pycore_interp.h" // _PyInterpreterState.float_state +#include "pycore_pystate.h" // _PyInterpreterState_GET() #include #include @@ -16,16 +18,9 @@ class float "PyObject *" "&PyFloat_Type" #include "clinic/floatobject.c.h" -/* Special free list - free_list is a singly-linked list of available PyFloatObjects, linked - via abuse of their ob_type members. -*/ - #ifndef PyFloat_MAXFREELIST -#define PyFloat_MAXFREELIST 100 +# define PyFloat_MAXFREELIST 100 #endif -static int numfree = 0; -static PyFloatObject *free_list = NULL; double PyFloat_GetMax(void) @@ -117,16 +112,19 @@ PyFloat_GetInfo(void) PyObject * PyFloat_FromDouble(double fval) { - PyFloatObject *op = free_list; + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_float_state *state = &interp->float_state; + PyFloatObject *op = state->free_list; if (op != NULL) { - free_list = (PyFloatObject *) Py_TYPE(op); - numfree--; - } else { - op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject)); - if (!op) + state->free_list = (PyFloatObject *) Py_TYPE(op); + state->numfree--; + } + else { + op = PyObject_Malloc(sizeof(PyFloatObject)); + if (!op) { return PyErr_NoMemory(); + } } - /* Inline PyObject_New */ (void)PyObject_INIT(op, &PyFloat_Type); op->ob_fval = fval; return (PyObject *) op; @@ -219,13 +217,15 @@ static void float_dealloc(PyFloatObject *op) { if (PyFloat_CheckExact(op)) { - if (numfree >= PyFloat_MAXFREELIST) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_float_state *state = &interp->float_state; + if (state->numfree >= PyFloat_MAXFREELIST) { PyObject_FREE(op); return; } - numfree++; - Py_SET_TYPE(op, (PyTypeObject *)free_list); - free_list = op; + state->numfree++; + Py_SET_TYPE(op, (PyTypeObject *)state->free_list); + state->free_list = op; } else Py_TYPE(op)->tp_free((PyObject *)op); @@ -1981,30 +1981,33 @@ _PyFloat_Init(void) } void -_PyFloat_ClearFreeList(void) +_PyFloat_ClearFreeList(PyThreadState *tstate) { - PyFloatObject *f = free_list, *next; + struct _Py_float_state *state = &tstate->interp->float_state; + PyFloatObject *f = state->free_list, *next; for (; f; f = next) { next = (PyFloatObject*) Py_TYPE(f); PyObject_FREE(f); } - free_list = NULL; - numfree = 0; + state->free_list = NULL; + state->numfree = 0; } void -_PyFloat_Fini(void) +_PyFloat_Fini(PyThreadState *tstate) { - _PyFloat_ClearFreeList(); + _PyFloat_ClearFreeList(tstate); } /* Print summary info about the state of the optimized allocator */ void _PyFloat_DebugMallocStats(FILE *out) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_float_state *state = &interp->float_state; _PyDebugAllocatorStats(out, "free PyFloatObject", - numfree, sizeof(PyFloatObject)); + state->numfree, sizeof(PyFloatObject)); } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 9da3fb0..716303c 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1261,9 +1261,9 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp) } _PyLong_Fini(tstate); + _PyFloat_Fini(tstate); if (is_main_interp) { - _PyFloat_Fini(); _PyDict_Fini(); _PySlice_Fini(); } -- cgit v0.12