Skip to content

Commit de68319

Browse files
corona10Glyphack
authored andcommitted
pythongh-111968: Use per-thread slice_cache in free-threading (pythongh-113972)
1 parent 084255b commit de68319

File tree

7 files changed

+28
-18
lines changed

7 files changed

+28
-18
lines changed

Include/internal/pycore_freelist.h

+9
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,19 @@ struct _Py_float_state {
5959
#endif
6060
};
6161

62+
struct _Py_slice_state {
63+
#ifdef WITH_FREELISTS
64+
/* Using a cache is very effective since typically only a single slice is
65+
created and then deleted again. */
66+
PySliceObject *slice_cache;
67+
#endif
68+
};
69+
6270
typedef struct _Py_freelist_state {
6371
struct _Py_float_state float_state;
6472
struct _Py_tuple_state tuple_state;
6573
struct _Py_list_state list_state;
74+
struct _Py_slice_state slice_state;
6675
} _PyFreeListState;
6776

6877
#ifdef __cplusplus

Include/internal/pycore_gc.h

+1
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ extern void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization);
249249
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);
252+
extern void _PySlice_ClearCache(_PyFreeListState *state);
252253
extern void _PyDict_ClearFreeList(PyInterpreterState *interp);
253254
extern void _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp);
254255
extern void _PyContext_ClearFreeList(PyInterpreterState *interp);

Include/internal/pycore_interp.h

-3
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,6 @@ struct _is {
187187
struct _Py_long_state long_state;
188188
struct _dtoa_state dtoa;
189189
struct _py_func_state func_state;
190-
/* Using a cache is very effective since typically only a single slice is
191-
created and then deleted again. */
192-
PySliceObject *slice_cache;
193190

194191
struct _Py_tuple_state tuple;
195192
struct _Py_dict_state dict_state;

Include/internal/pycore_sliceobject.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern "C" {
1111

1212
/* runtime lifecycle */
1313

14-
extern void _PySlice_Fini(PyInterpreterState *);
14+
extern void _PySlice_Fini(_PyFreeListState *);
1515

1616
extern PyObject *
1717
_PyBuildSlice_ConsumeRefs(PyObject *start, PyObject *stop);

Objects/sliceobject.c

+15-11
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,20 @@ PyObject _Py_EllipsisObject = _PyObject_HEAD_INIT(&PyEllipsis_Type);
103103

104104
/* Slice object implementation */
105105

106-
107-
void _PySlice_Fini(PyInterpreterState *interp)
106+
void _PySlice_ClearCache(_PyFreeListState *state)
108107
{
109-
PySliceObject *obj = interp->slice_cache;
108+
PySliceObject *obj = state->slice_state.slice_cache;
110109
if (obj != NULL) {
111-
interp->slice_cache = NULL;
110+
state->slice_state.slice_cache = NULL;
112111
PyObject_GC_Del(obj);
113112
}
114113
}
115114

115+
void _PySlice_Fini(_PyFreeListState *state)
116+
{
117+
_PySlice_ClearCache(state);
118+
}
119+
116120
/* start, stop, and step are python objects with None indicating no
117121
index is present.
118122
*/
@@ -122,11 +126,11 @@ _PyBuildSlice_Consume2(PyObject *start, PyObject *stop, PyObject *step)
122126
{
123127
assert(start != NULL && stop != NULL && step != NULL);
124128

125-
PyInterpreterState *interp = _PyInterpreterState_GET();
129+
_PyFreeListState *state = _PyFreeListState_GET();
126130
PySliceObject *obj;
127-
if (interp->slice_cache != NULL) {
128-
obj = interp->slice_cache;
129-
interp->slice_cache = NULL;
131+
if (state->slice_state.slice_cache != NULL) {
132+
obj = state->slice_state.slice_cache;
133+
state->slice_state.slice_cache = NULL;
130134
_Py_NewReference((PyObject *)obj);
131135
}
132136
else {
@@ -354,13 +358,13 @@ Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).");
354358
static void
355359
slice_dealloc(PySliceObject *r)
356360
{
357-
PyInterpreterState *interp = _PyInterpreterState_GET();
361+
_PyFreeListState *state = _PyFreeListState_GET();
358362
_PyObject_GC_UNTRACK(r);
359363
Py_DECREF(r->step);
360364
Py_DECREF(r->start);
361365
Py_DECREF(r->stop);
362-
if (interp->slice_cache == NULL) {
363-
interp->slice_cache = r;
366+
if (state->slice_state.slice_cache == NULL) {
367+
state->slice_state.slice_cache = r;
364368
}
365369
else {
366370
PyObject_GC_Del(r);

Python/pylifecycle.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -1752,15 +1752,13 @@ finalize_interp_types(PyInterpreterState *interp)
17521752
_PyUnicode_ClearInterned(interp);
17531753

17541754
_PyDict_Fini(interp);
1755-
1756-
_PySlice_Fini(interp);
1757-
17581755
_PyUnicode_Fini(interp);
17591756

17601757
_PyFreeListState *state = _PyFreeListState_GET();
17611758
_PyTuple_Fini(state);
17621759
_PyList_Fini(state);
17631760
_PyFloat_Fini(state);
1761+
_PySlice_Fini(state);
17641762

17651763
#ifdef Py_DEBUG
17661764
_PyStaticObjects_CheckRefcnt(interp);

Python/pystate.c

+1
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,7 @@ PyThreadState_Clear(PyThreadState *tstate)
15491549
// Each thread should clear own freelists in free-threading builds.
15501550
_PyFreeListState *freelist_state = &((_PyThreadStateImpl*)tstate)->freelist_state;
15511551
_Py_ClearFreeLists(freelist_state, 0);
1552+
_PySlice_ClearCache(freelist_state);
15521553
#endif
15531554

15541555
_PyThreadState_ClearMimallocHeaps(tstate);

0 commit comments

Comments
 (0)