[Python-checkins] bpo-40521: Make float free list per-interpreter (GH-20636)

Victor Stinner webhook-mailer at python.org
Thu Jun 4 18:50:20 EDT 2020


https://github.com/python/cpython/commit/2ba59370c3dda2ac229c14510e53a05074b133d1
commit: 2ba59370c3dda2ac229c14510e53a05074b133d1
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-06-05T00:50:05+02:00
summary:

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().

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

diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index e8e5d32977095..f90d80be16878 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 b90bfbe797b58..c0eed00f36581 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 3f2ff5bfd2410..2643abca0f553 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 f364d36a135f2..016f11668ee44 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 1f5aa936e41c7..0bad0f8917f37 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 868b7298a9e8d..d72fd21f95faf 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 <ctype.h>
 #include <float.h>
@@ -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 9da3fb09c38ba..716303cffc764 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();
     }



More information about the Python-checkins mailing list