Skip to content

Commit 6f94e65

Browse files
corona10Glyphack
authored andcommitted
pythongh-111968: Use per-thread freelists for tuple in free-threading (pythongh-113921)
1 parent 6a8f1d3 commit 6f94e65

File tree

8 files changed

+45
-70
lines changed

8 files changed

+45
-70
lines changed

Include/internal/pycore_freelist.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,19 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
// PyTuple_MAXSAVESIZE - largest tuple to save on free list
12+
// PyTuple_MAXFREELIST - maximum number of tuples of each size to save
13+
1114
#ifdef WITH_FREELISTS
1215
// with freelists
16+
# define PyTuple_MAXSAVESIZE 20
17+
# define PyTuple_NFREELISTS PyTuple_MAXSAVESIZE
18+
# define PyTuple_MAXFREELIST 2000
1319
# define PyList_MAXFREELIST 80
1420
# define PyFloat_MAXFREELIST 100
1521
#else
22+
# define PyTuple_NFREELISTS 0
23+
# define PyTuple_MAXFREELIST 0
1624
# define PyList_MAXFREELIST 0
1725
# define PyFloat_MAXFREELIST 0
1826
#endif
@@ -24,6 +32,23 @@ struct _Py_list_state {
2432
#endif
2533
};
2634

35+
struct _Py_tuple_state {
36+
#if WITH_FREELISTS
37+
/* There is one freelist for each size from 1 to PyTuple_MAXSAVESIZE.
38+
The empty tuple is handled separately.
39+
40+
Each tuple stored in the array is the head of the linked list
41+
(and the next available tuple) for that size. The actual tuple
42+
object is used as the linked list node, with its first item
43+
(ob_item[0]) pointing to the next node (i.e. the previous head).
44+
Each linked list is initially NULL. */
45+
PyTupleObject *free_list[PyTuple_NFREELISTS];
46+
int numfree[PyTuple_NFREELISTS];
47+
#else
48+
char _unused; // Empty structs are not allowed.
49+
#endif
50+
};
51+
2752
struct _Py_float_state {
2853
#ifdef WITH_FREELISTS
2954
/* Special free list
@@ -36,6 +61,7 @@ struct _Py_float_state {
3661

3762
typedef struct _Py_freelist_state {
3863
struct _Py_float_state float_state;
64+
struct _Py_tuple_state tuple_state;
3965
struct _Py_list_state list_state;
4066
} _PyFreeListState;
4167

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs);
246246
// Functions to clear types free lists
247247
extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp);
248248
extern void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization);
249-
extern void _PyTuple_ClearFreeList(PyInterpreterState *interp);
249+
extern void _PyTuple_ClearFreeList(_PyFreeListState *state, int is_finalization);
250250
extern void _PyFloat_ClearFreeList(_PyFreeListState *state, int is_finalization);
251251
extern void _PyList_ClearFreeList(_PyFreeListState *state, int is_finalization);
252252
extern void _PyDict_ClearFreeList(PyInterpreterState *interp);

Include/internal/pycore_tuple.h

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,59 +14,16 @@ extern void _PyTuple_DebugMallocStats(FILE *out);
1414
/* runtime lifecycle */
1515

1616
extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);
17-
extern void _PyTuple_Fini(PyInterpreterState *);
17+
extern void _PyTuple_Fini(_PyFreeListState *);
1818

1919

2020
/* other API */
2121

22-
// PyTuple_MAXSAVESIZE - largest tuple to save on free list
23-
// PyTuple_MAXFREELIST - maximum number of tuples of each size to save
24-
25-
#if defined(PyTuple_MAXSAVESIZE) && PyTuple_MAXSAVESIZE <= 0
26-
// A build indicated that tuple freelists should not be used.
27-
# define PyTuple_NFREELISTS 0
28-
# undef PyTuple_MAXSAVESIZE
29-
# undef PyTuple_MAXFREELIST
30-
31-
#elif !defined(WITH_FREELISTS)
32-
# define PyTuple_NFREELISTS 0
33-
# undef PyTuple_MAXSAVESIZE
34-
# undef PyTuple_MAXFREELIST
35-
36-
#else
37-
// We are using a freelist for tuples.
38-
# ifndef PyTuple_MAXSAVESIZE
39-
# define PyTuple_MAXSAVESIZE 20
40-
# endif
41-
# define PyTuple_NFREELISTS PyTuple_MAXSAVESIZE
42-
# ifndef PyTuple_MAXFREELIST
43-
# define PyTuple_MAXFREELIST 2000
44-
# endif
45-
#endif
46-
47-
struct _Py_tuple_state {
48-
#if PyTuple_NFREELISTS > 0
49-
/* There is one freelist for each size from 1 to PyTuple_MAXSAVESIZE.
50-
The empty tuple is handled separately.
51-
52-
Each tuple stored in the array is the head of the linked list
53-
(and the next available tuple) for that size. The actual tuple
54-
object is used as the linked list node, with its first item
55-
(ob_item[0]) pointing to the next node (i.e. the previous head).
56-
Each linked list is initially NULL. */
57-
PyTupleObject *free_list[PyTuple_NFREELISTS];
58-
int numfree[PyTuple_NFREELISTS];
59-
#else
60-
char _unused; // Empty structs are not allowed.
61-
#endif
62-
};
63-
6422
#define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item)
6523

6624
extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
6725
extern PyObject *_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);
6826

69-
7027
typedef struct {
7128
PyObject_HEAD
7229
Py_ssize_t it_index;

Objects/tupleobject.c

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -962,18 +962,18 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
962962
}
963963

964964

965-
static void maybe_freelist_clear(PyInterpreterState *, int);
965+
static void maybe_freelist_clear(_PyFreeListState *, int);
966966

967967
void
968-
_PyTuple_Fini(PyInterpreterState *interp)
968+
_PyTuple_Fini(_PyFreeListState *state)
969969
{
970-
maybe_freelist_clear(interp, 1);
970+
maybe_freelist_clear(state, 1);
971971
}
972972

973973
void
974-
_PyTuple_ClearFreeList(PyInterpreterState *interp)
974+
_PyTuple_ClearFreeList(_PyFreeListState *state, int is_finalization)
975975
{
976-
maybe_freelist_clear(interp, 0);
976+
maybe_freelist_clear(state, is_finalization);
977977
}
978978

979979
/*********************** Tuple Iterator **************************/
@@ -1125,18 +1125,14 @@ tuple_iter(PyObject *seq)
11251125
* freelists *
11261126
*************/
11271127

1128-
#define STATE (interp->tuple)
1128+
#define STATE (state->tuple_state)
11291129
#define FREELIST_FINALIZED (STATE.numfree[0] < 0)
11301130

11311131
static inline PyTupleObject *
11321132
maybe_freelist_pop(Py_ssize_t size)
11331133
{
1134-
#if PyTuple_NFREELISTS > 0
1135-
PyInterpreterState *interp = _PyInterpreterState_GET();
1136-
#ifdef Py_DEBUG
1137-
/* maybe_freelist_pop() must not be called after maybe_freelist_fini(). */
1138-
assert(!FREELIST_FINALIZED);
1139-
#endif
1134+
#ifdef WITH_FREELISTS
1135+
_PyFreeListState *state = _PyFreeListState_GET();
11401136
if (size == 0) {
11411137
return NULL;
11421138
}
@@ -1169,18 +1165,15 @@ maybe_freelist_pop(Py_ssize_t size)
11691165
static inline int
11701166
maybe_freelist_push(PyTupleObject *op)
11711167
{
1172-
#if PyTuple_NFREELISTS > 0
1173-
PyInterpreterState *interp = _PyInterpreterState_GET();
1174-
#ifdef Py_DEBUG
1175-
/* maybe_freelist_push() must not be called after maybe_freelist_fini(). */
1176-
assert(!FREELIST_FINALIZED);
1177-
#endif
1168+
#ifdef WITH_FREELISTS
1169+
_PyFreeListState *state = _PyFreeListState_GET();
11781170
if (Py_SIZE(op) == 0) {
11791171
return 0;
11801172
}
11811173
Py_ssize_t index = Py_SIZE(op) - 1;
11821174
if (index < PyTuple_NFREELISTS
11831175
&& STATE.numfree[index] < PyTuple_MAXFREELIST
1176+
&& STATE.numfree[index] >= 0
11841177
&& Py_IS_TYPE(op, &PyTuple_Type))
11851178
{
11861179
/* op is the head of a linked list, with the first item
@@ -1196,9 +1189,9 @@ maybe_freelist_push(PyTupleObject *op)
11961189
}
11971190

11981191
static void
1199-
maybe_freelist_clear(PyInterpreterState *interp, int fini)
1192+
maybe_freelist_clear(_PyFreeListState *state, int fini)
12001193
{
1201-
#if PyTuple_NFREELISTS > 0
1194+
#ifdef WITH_FREELISTS
12021195
for (Py_ssize_t i = 0; i < PyTuple_NFREELISTS; i++) {
12031196
PyTupleObject *p = STATE.free_list[i];
12041197
STATE.free_list[i] = NULL;
@@ -1216,8 +1209,8 @@ maybe_freelist_clear(PyInterpreterState *interp, int fini)
12161209
void
12171210
_PyTuple_DebugMallocStats(FILE *out)
12181211
{
1219-
#if PyTuple_NFREELISTS > 0
1220-
PyInterpreterState *interp = _PyInterpreterState_GET();
1212+
#ifdef WITH_FREELISTS
1213+
_PyFreeListState *state = _PyFreeListState_GET();
12211214
for (int i = 0; i < PyTuple_NFREELISTS; i++) {
12221215
int len = i + 1;
12231216
char buf[128];

Python/gc_free_threading.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
void
1515
_PyGC_ClearAllFreeLists(PyInterpreterState *interp)
1616
{
17-
_PyTuple_ClearFreeList(interp);
1817
_PyDict_ClearFreeList(interp);
1918
_PyAsyncGen_ClearFreeLists(interp);
2019
_PyContext_ClearFreeList(interp);

Python/gc_gil.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
void
1212
_PyGC_ClearAllFreeLists(PyInterpreterState *interp)
1313
{
14-
_PyTuple_ClearFreeList(interp);
1514
_PyDict_ClearFreeList(interp);
1615
_PyAsyncGen_ClearFreeLists(interp);
1716
_PyContext_ClearFreeList(interp);

Python/pylifecycle.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1752,13 +1752,13 @@ finalize_interp_types(PyInterpreterState *interp)
17521752
_PyUnicode_ClearInterned(interp);
17531753

17541754
_PyDict_Fini(interp);
1755-
_PyTuple_Fini(interp);
17561755

17571756
_PySlice_Fini(interp);
17581757

17591758
_PyUnicode_Fini(interp);
17601759

17611760
_PyFreeListState *state = _PyFreeListState_GET();
1761+
_PyTuple_Fini(state);
17621762
_PyList_Fini(state);
17631763
_PyFloat_Fini(state);
17641764

Python/pystate.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,7 @@ void
14591459
_Py_ClearFreeLists(_PyFreeListState *state, int is_finalization)
14601460
{
14611461
_PyFloat_ClearFreeList(state, is_finalization);
1462+
_PyTuple_ClearFreeList(state, is_finalization);
14621463
_PyList_ClearFreeList(state, is_finalization);
14631464
}
14641465

0 commit comments

Comments
 (0)