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().
This commit is contained in:
parent
69ac6e58fd
commit
2ba59370c3
|
@ -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);
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
return PyErr_NoMemory();
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue