Skip to content

Commit 69ac6e5

Browse files
authored
bpo-40521: Make tuple free list per-interpreter (GH-20247)
Each interpreter now has its own tuple free lists: * Move tuple numfree and free_list arrays into PyInterpreterState. * Define PyTuple_MAXSAVESIZE and PyTuple_MAXFREELIST macros in pycore_interp.h. * Add _Py_tuple_state structure. Pass it explicitly to tuple_alloc(). * Add tstate parameter to _PyTuple_ClearFreeList() * Each interpreter now has its own empty tuple singleton.
1 parent dc24b8a commit 69ac6e5

File tree

7 files changed

+82
-62
lines changed

7 files changed

+82
-62
lines changed

Include/internal/pycore_gc.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
166166

167167
// Functions to clear types free lists
168168
extern void _PyFrame_ClearFreeList(void);
169-
extern void _PyTuple_ClearFreeList(void);
169+
extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
170170
extern void _PyFloat_ClearFreeList(void);
171171
extern void _PyList_ClearFreeList(void);
172172
extern void _PyDict_ClearFreeList(void);

Include/internal/pycore_interp.h

+21
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,26 @@ struct _Py_unicode_state {
6464
struct _Py_unicode_fs_codec fs_codec;
6565
};
6666

67+
/* Speed optimization to avoid frequent malloc/free of small tuples */
68+
#ifndef PyTuple_MAXSAVESIZE
69+
// Largest tuple to save on free list
70+
# define PyTuple_MAXSAVESIZE 20
71+
#endif
72+
#ifndef PyTuple_MAXFREELIST
73+
// Maximum number of tuples of each size to save
74+
# define PyTuple_MAXFREELIST 2000
75+
#endif
76+
77+
struct _Py_tuple_state {
78+
#if PyTuple_MAXSAVESIZE > 0
79+
/* Entries 1 up to PyTuple_MAXSAVESIZE are free lists,
80+
entry 0 is the empty tuple () of which at most one instance
81+
will be allocated. */
82+
PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
83+
int numfree[PyTuple_MAXSAVESIZE];
84+
#endif
85+
};
86+
6787

6888
/* interpreter state */
6989

@@ -157,6 +177,7 @@ struct _is {
157177
*/
158178
PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
159179
#endif
180+
struct _Py_tuple_state tuple;
160181
};
161182

162183
/* Used by _PyImport_Cleanup() */

Include/internal/pycore_pylifecycle.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ extern PyStatus _PyGC_Init(PyThreadState *tstate);
6060

6161
extern void _PyFrame_Fini(void);
6262
extern void _PyDict_Fini(void);
63-
extern void _PyTuple_Fini(void);
63+
extern void _PyTuple_Fini(PyThreadState *tstate);
6464
extern void _PyList_Fini(void);
6565
extern void _PySet_Fini(void);
6666
extern void _PyBytes_Fini(void);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Each interpreter now has its own tuple free lists and empty tuple singleton.

Modules/gcmodule.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1025,8 +1025,9 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
10251025
static void
10261026
clear_freelists(void)
10271027
{
1028+
PyThreadState *tstate = _PyThreadState_GET();
10281029
_PyFrame_ClearFreeList();
1029-
_PyTuple_ClearFreeList();
1030+
_PyTuple_ClearFreeList(tstate);
10301031
_PyFloat_ClearFreeList();
10311032
_PyList_ClearFreeList();
10321033
_PyDict_ClearFreeList();

Objects/tupleobject.c

+53-58
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,6 @@ class tuple "PyTupleObject *" "&PyTuple_Type"
1414

1515
#include "clinic/tupleobject.c.h"
1616

17-
/* Speed optimization to avoid frequent malloc/free of small tuples */
18-
#ifndef PyTuple_MAXSAVESIZE
19-
#define PyTuple_MAXSAVESIZE 20 /* Largest tuple to save on free list */
20-
#endif
21-
#ifndef PyTuple_MAXFREELIST
22-
#define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */
23-
#endif
24-
25-
/* bpo-40521: tuple free lists are shared by all interpreters. */
26-
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
27-
# undef PyTuple_MAXSAVESIZE
28-
# define PyTuple_MAXSAVESIZE 0
29-
#endif
30-
31-
#if PyTuple_MAXSAVESIZE > 0
32-
/* Entries 1 up to PyTuple_MAXSAVESIZE are free lists, entry 0 is the empty
33-
tuple () of which at most one instance will be allocated.
34-
*/
35-
static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
36-
static int numfree[PyTuple_MAXSAVESIZE];
37-
#endif
38-
3917
static inline void
4018
tuple_gc_track(PyTupleObject *op)
4119
{
@@ -47,14 +25,14 @@ void
4725
_PyTuple_DebugMallocStats(FILE *out)
4826
{
4927
#if PyTuple_MAXSAVESIZE > 0
50-
int i;
51-
char buf[128];
52-
for (i = 1; i < PyTuple_MAXSAVESIZE; i++) {
28+
PyInterpreterState *interp = _PyInterpreterState_GET();
29+
struct _Py_tuple_state *state = &interp->tuple;
30+
for (int i = 1; i < PyTuple_MAXSAVESIZE; i++) {
31+
char buf[128];
5332
PyOS_snprintf(buf, sizeof(buf),
5433
"free %d-sized PyTupleObject", i);
55-
_PyDebugAllocatorStats(out,
56-
buf,
57-
numfree[i], _PyObject_VAR_SIZE(&PyTuple_Type, i));
34+
_PyDebugAllocatorStats(out, buf, state->numfree[i],
35+
_PyObject_VAR_SIZE(&PyTuple_Type, i));
5836
}
5937
#endif
6038
}
@@ -68,18 +46,18 @@ _PyTuple_DebugMallocStats(FILE *out)
6846
which wraps this function).
6947
*/
7048
static PyTupleObject *
71-
tuple_alloc(Py_ssize_t size)
49+
tuple_alloc(struct _Py_tuple_state *state, Py_ssize_t size)
7250
{
7351
PyTupleObject *op;
7452
if (size < 0) {
7553
PyErr_BadInternalCall();
7654
return NULL;
7755
}
7856
#if PyTuple_MAXSAVESIZE > 0
79-
if (size < PyTuple_MAXSAVESIZE && (op = free_list[size]) != NULL) {
57+
if (size < PyTuple_MAXSAVESIZE && (op = state->free_list[size]) != NULL) {
8058
assert(size != 0);
81-
free_list[size] = (PyTupleObject *) op->ob_item[0];
82-
numfree[size]--;
59+
state->free_list[size] = (PyTupleObject *) op->ob_item[0];
60+
state->numfree[size]--;
8361
/* Inline PyObject_InitVar */
8462
#ifdef Py_TRACE_REFS
8563
Py_SET_SIZE(op, size);
@@ -107,13 +85,15 @@ PyTuple_New(Py_ssize_t size)
10785
{
10886
PyTupleObject *op;
10987
#if PyTuple_MAXSAVESIZE > 0
110-
if (size == 0 && free_list[0]) {
111-
op = free_list[0];
88+
PyInterpreterState *interp = _PyInterpreterState_GET();
89+
struct _Py_tuple_state *state = &interp->tuple;
90+
if (size == 0 && state->free_list[0]) {
91+
op = state->free_list[0];
11292
Py_INCREF(op);
11393
return (PyObject *) op;
11494
}
11595
#endif
116-
op = tuple_alloc(size);
96+
op = tuple_alloc(state, size);
11797
if (op == NULL) {
11898
return NULL;
11999
}
@@ -122,8 +102,8 @@ PyTuple_New(Py_ssize_t size)
122102
}
123103
#if PyTuple_MAXSAVESIZE > 0
124104
if (size == 0) {
125-
free_list[0] = op;
126-
++numfree[0];
105+
state->free_list[0] = op;
106+
++state->numfree[0];
127107
Py_INCREF(op); /* extra INCREF so that this is never freed */
128108
}
129109
#endif
@@ -210,8 +190,11 @@ PyTuple_Pack(Py_ssize_t n, ...)
210190
return PyTuple_New(0);
211191
}
212192

193+
PyInterpreterState *interp = _PyInterpreterState_GET();
194+
struct _Py_tuple_state *state = &interp->tuple;
195+
213196
va_start(vargs, n);
214-
PyTupleObject *result = tuple_alloc(n);
197+
PyTupleObject *result = tuple_alloc(state, n);
215198
if (result == NULL) {
216199
va_end(vargs);
217200
return NULL;
@@ -233,22 +216,24 @@ PyTuple_Pack(Py_ssize_t n, ...)
233216
static void
234217
tupledealloc(PyTupleObject *op)
235218
{
236-
Py_ssize_t i;
237219
Py_ssize_t len = Py_SIZE(op);
238220
PyObject_GC_UnTrack(op);
239221
Py_TRASHCAN_BEGIN(op, tupledealloc)
240222
if (len > 0) {
241-
i = len;
242-
while (--i >= 0)
223+
Py_ssize_t i = len;
224+
while (--i >= 0) {
243225
Py_XDECREF(op->ob_item[i]);
226+
}
244227
#if PyTuple_MAXSAVESIZE > 0
228+
PyInterpreterState *interp = _PyInterpreterState_GET();
229+
struct _Py_tuple_state *state = &interp->tuple;
245230
if (len < PyTuple_MAXSAVESIZE &&
246-
numfree[len] < PyTuple_MAXFREELIST &&
231+
state->numfree[len] < PyTuple_MAXFREELIST &&
247232
Py_IS_TYPE(op, &PyTuple_Type))
248233
{
249-
op->ob_item[0] = (PyObject *) free_list[len];
250-
numfree[len]++;
251-
free_list[len] = op;
234+
op->ob_item[0] = (PyObject *) state->free_list[len];
235+
state->numfree[len]++;
236+
state->free_list[len] = op;
252237
goto done; /* return */
253238
}
254239
#endif
@@ -423,7 +408,9 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
423408
return PyTuple_New(0);
424409
}
425410

426-
PyTupleObject *tuple = tuple_alloc(n);
411+
PyInterpreterState *interp = _PyInterpreterState_GET();
412+
struct _Py_tuple_state *state = &interp->tuple;
413+
PyTupleObject *tuple = tuple_alloc(state, n);
427414
if (tuple == NULL) {
428415
return NULL;
429416
}
@@ -481,7 +468,8 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
481468
Py_TYPE(bb)->tp_name);
482469
return NULL;
483470
}
484-
#define b ((PyTupleObject *)bb)
471+
PyTupleObject *b = (PyTupleObject *)bb;
472+
485473
if (Py_SIZE(b) == 0 && PyTuple_CheckExact(a)) {
486474
Py_INCREF(a);
487475
return (PyObject *)a;
@@ -492,7 +480,9 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
492480
return PyTuple_New(0);
493481
}
494482

495-
np = tuple_alloc(size);
483+
PyInterpreterState *interp = _PyInterpreterState_GET();
484+
struct _Py_tuple_state *state = &interp->tuple;
485+
np = tuple_alloc(state, size);
496486
if (np == NULL) {
497487
return NULL;
498488
}
@@ -512,7 +502,6 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
512502
}
513503
tuple_gc_track(np);
514504
return (PyObject *)np;
515-
#undef b
516505
}
517506

518507
static PyObject *
@@ -536,7 +525,9 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
536525
if (n > PY_SSIZE_T_MAX / Py_SIZE(a))
537526
return PyErr_NoMemory();
538527
size = Py_SIZE(a) * n;
539-
np = tuple_alloc(size);
528+
PyInterpreterState *interp = _PyInterpreterState_GET();
529+
struct _Py_tuple_state *state = &interp->tuple;
530+
np = tuple_alloc(state, size);
540531
if (np == NULL)
541532
return NULL;
542533
p = np->ob_item;
@@ -801,7 +792,9 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
801792
return (PyObject *)self;
802793
}
803794
else {
804-
PyTupleObject* result = tuple_alloc(slicelength);
795+
PyInterpreterState *interp = _PyInterpreterState_GET();
796+
struct _Py_tuple_state *state = &interp->tuple;
797+
PyTupleObject* result = tuple_alloc(state, slicelength);
805798
if (!result) return NULL;
806799

807800
src = self->ob_item;
@@ -963,13 +956,14 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
963956
}
964957

965958
void
966-
_PyTuple_ClearFreeList(void)
959+
_PyTuple_ClearFreeList(PyThreadState *tstate)
967960
{
968961
#if PyTuple_MAXSAVESIZE > 0
962+
struct _Py_tuple_state *state = &tstate->interp->tuple;
969963
for (Py_ssize_t i = 1; i < PyTuple_MAXSAVESIZE; i++) {
970-
PyTupleObject *p = free_list[i];
971-
free_list[i] = NULL;
972-
numfree[i] = 0;
964+
PyTupleObject *p = state->free_list[i];
965+
state->free_list[i] = NULL;
966+
state->numfree[i] = 0;
973967
while (p) {
974968
PyTupleObject *q = p;
975969
p = (PyTupleObject *)(p->ob_item[0]);
@@ -981,14 +975,15 @@ _PyTuple_ClearFreeList(void)
981975
}
982976

983977
void
984-
_PyTuple_Fini(void)
978+
_PyTuple_Fini(PyThreadState *tstate)
985979
{
986980
#if PyTuple_MAXSAVESIZE > 0
981+
struct _Py_tuple_state *state = &tstate->interp->tuple;
987982
/* empty tuples are used all over the place and applications may
988983
* rely on the fact that an empty tuple is a singleton. */
989-
Py_CLEAR(free_list[0]);
984+
Py_CLEAR(state->free_list[0]);
990985

991-
_PyTuple_ClearFreeList();
986+
_PyTuple_ClearFreeList(tstate);
992987
#endif
993988
}
994989

Python/pylifecycle.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,9 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12521252
if (is_main_interp) {
12531253
/* Sundry finalizers */
12541254
_PyFrame_Fini();
1255-
_PyTuple_Fini();
1255+
}
1256+
_PyTuple_Fini(tstate);
1257+
if (is_main_interp) {
12561258
_PyList_Fini();
12571259
_PySet_Fini();
12581260
_PyBytes_Fini();

0 commit comments

Comments
 (0)