Skip to content

Commit 2ba5937

Browse files
authored
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().
1 parent 69ac6e5 commit 2ba5937

File tree

7 files changed

+44
-31
lines changed

7 files changed

+44
-31
lines changed

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
167167
// Functions to clear types free lists
168168
extern void _PyFrame_ClearFreeList(void);
169169
extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
170-
extern void _PyFloat_ClearFreeList(void);
170+
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
171171
extern void _PyList_ClearFreeList(void);
172172
extern void _PyDict_ClearFreeList(void);
173173
extern void _PyAsyncGen_ClearFreeLists(void);

Include/internal/pycore_interp.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ struct _Py_tuple_state {
8484
#endif
8585
};
8686

87+
struct _Py_float_state {
88+
/* Special free list
89+
free_list is a singly-linked list of available PyFloatObjects,
90+
linked via abuse of their ob_type members. */
91+
int numfree;
92+
PyFloatObject *free_list;
93+
};
94+
8795

8896
/* interpreter state */
8997

@@ -178,6 +186,7 @@ struct _is {
178186
PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
179187
#endif
180188
struct _Py_tuple_state tuple;
189+
struct _Py_float_state float_state;
181190
};
182191

183192
/* Used by _PyImport_Cleanup() */

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ extern void _PyTuple_Fini(PyThreadState *tstate);
6464
extern void _PyList_Fini(void);
6565
extern void _PySet_Fini(void);
6666
extern void _PyBytes_Fini(void);
67-
extern void _PyFloat_Fini(void);
67+
extern void _PyFloat_Fini(PyThreadState *tstate);
6868
extern void _PySlice_Fini(void);
6969
extern void _PyAsyncGen_Fini(void);
7070

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Each interpreter now has its own tuple free lists and empty tuple singleton.
1+
Tuple free lists, empty tuple singleton, and float free list are no longer
2+
shared by all interpreters: each interpreter now its own free lists.

Modules/gcmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ clear_freelists(void)
10281028
PyThreadState *tstate = _PyThreadState_GET();
10291029
_PyFrame_ClearFreeList();
10301030
_PyTuple_ClearFreeList(tstate);
1031-
_PyFloat_ClearFreeList();
1031+
_PyFloat_ClearFreeList(tstate);
10321032
_PyList_ClearFreeList();
10331033
_PyDict_ClearFreeList();
10341034
_PyAsyncGen_ClearFreeLists();

Objects/floatobject.c

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include "Python.h"
77
#include "pycore_dtoa.h"
8+
#include "pycore_interp.h" // _PyInterpreterState.float_state
9+
#include "pycore_pystate.h" // _PyInterpreterState_GET()
810

911
#include <ctype.h>
1012
#include <float.h>
@@ -16,16 +18,9 @@ class float "PyObject *" "&PyFloat_Type"
1618

1719
#include "clinic/floatobject.c.h"
1820

19-
/* Special free list
20-
free_list is a singly-linked list of available PyFloatObjects, linked
21-
via abuse of their ob_type members.
22-
*/
23-
2421
#ifndef PyFloat_MAXFREELIST
25-
#define PyFloat_MAXFREELIST 100
22+
# define PyFloat_MAXFREELIST 100
2623
#endif
27-
static int numfree = 0;
28-
static PyFloatObject *free_list = NULL;
2924

3025
double
3126
PyFloat_GetMax(void)
@@ -117,16 +112,19 @@ PyFloat_GetInfo(void)
117112
PyObject *
118113
PyFloat_FromDouble(double fval)
119114
{
120-
PyFloatObject *op = free_list;
115+
PyInterpreterState *interp = _PyInterpreterState_GET();
116+
struct _Py_float_state *state = &interp->float_state;
117+
PyFloatObject *op = state->free_list;
121118
if (op != NULL) {
122-
free_list = (PyFloatObject *) Py_TYPE(op);
123-
numfree--;
124-
} else {
125-
op = (PyFloatObject*) PyObject_MALLOC(sizeof(PyFloatObject));
126-
if (!op)
119+
state->free_list = (PyFloatObject *) Py_TYPE(op);
120+
state->numfree--;
121+
}
122+
else {
123+
op = PyObject_Malloc(sizeof(PyFloatObject));
124+
if (!op) {
127125
return PyErr_NoMemory();
126+
}
128127
}
129-
/* Inline PyObject_New */
130128
(void)PyObject_INIT(op, &PyFloat_Type);
131129
op->ob_fval = fval;
132130
return (PyObject *) op;
@@ -219,13 +217,15 @@ static void
219217
float_dealloc(PyFloatObject *op)
220218
{
221219
if (PyFloat_CheckExact(op)) {
222-
if (numfree >= PyFloat_MAXFREELIST) {
220+
PyInterpreterState *interp = _PyInterpreterState_GET();
221+
struct _Py_float_state *state = &interp->float_state;
222+
if (state->numfree >= PyFloat_MAXFREELIST) {
223223
PyObject_FREE(op);
224224
return;
225225
}
226-
numfree++;
227-
Py_SET_TYPE(op, (PyTypeObject *)free_list);
228-
free_list = op;
226+
state->numfree++;
227+
Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
228+
state->free_list = op;
229229
}
230230
else
231231
Py_TYPE(op)->tp_free((PyObject *)op);
@@ -1981,30 +1981,33 @@ _PyFloat_Init(void)
19811981
}
19821982

19831983
void
1984-
_PyFloat_ClearFreeList(void)
1984+
_PyFloat_ClearFreeList(PyThreadState *tstate)
19851985
{
1986-
PyFloatObject *f = free_list, *next;
1986+
struct _Py_float_state *state = &tstate->interp->float_state;
1987+
PyFloatObject *f = state->free_list, *next;
19871988
for (; f; f = next) {
19881989
next = (PyFloatObject*) Py_TYPE(f);
19891990
PyObject_FREE(f);
19901991
}
1991-
free_list = NULL;
1992-
numfree = 0;
1992+
state->free_list = NULL;
1993+
state->numfree = 0;
19931994
}
19941995

19951996
void
1996-
_PyFloat_Fini(void)
1997+
_PyFloat_Fini(PyThreadState *tstate)
19971998
{
1998-
_PyFloat_ClearFreeList();
1999+
_PyFloat_ClearFreeList(tstate);
19992000
}
20002001

20012002
/* Print summary info about the state of the optimized allocator */
20022003
void
20032004
_PyFloat_DebugMallocStats(FILE *out)
20042005
{
2006+
PyInterpreterState *interp = _PyInterpreterState_GET();
2007+
struct _Py_float_state *state = &interp->float_state;
20052008
_PyDebugAllocatorStats(out,
20062009
"free PyFloatObject",
2007-
numfree, sizeof(PyFloatObject));
2010+
state->numfree, sizeof(PyFloatObject));
20082011
}
20092012

20102013

Python/pylifecycle.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1261,9 +1261,9 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12611261
}
12621262

12631263
_PyLong_Fini(tstate);
1264+
_PyFloat_Fini(tstate);
12641265

12651266
if (is_main_interp) {
1266-
_PyFloat_Fini();
12671267
_PyDict_Fini();
12681268
_PySlice_Fini();
12691269
}

0 commit comments

Comments
 (0)