]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111968: Use per-thread freelists for tuple in free-threading (gh-113921)
authorDonghee Na <donghee.na@python.org>
Thu, 11 Jan 2024 18:46:28 +0000 (03:46 +0900)
committerGitHub <noreply@github.com>
Thu, 11 Jan 2024 18:46:28 +0000 (03:46 +0900)
Include/internal/pycore_freelist.h
Include/internal/pycore_gc.h
Include/internal/pycore_tuple.h
Objects/tupleobject.c
Python/gc_free_threading.c
Python/gc_gil.c
Python/pylifecycle.c
Python/pystate.c

index d41153d26a25468f71059cb0ce2a22940a0565df..34009435910d99f7d9e044a80fded83627d252b7 100644 (file)
@@ -8,11 +8,19 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+// PyTuple_MAXSAVESIZE - largest tuple to save on free list
+// PyTuple_MAXFREELIST - maximum number of tuples of each size to save
+
 #ifdef WITH_FREELISTS
 // with freelists
+#  define PyTuple_MAXSAVESIZE 20
+#  define PyTuple_NFREELISTS PyTuple_MAXSAVESIZE
+#  define PyTuple_MAXFREELIST 2000
 #  define PyList_MAXFREELIST 80
 #  define PyFloat_MAXFREELIST 100
 #else
+#  define PyTuple_NFREELISTS 0
+#  define PyTuple_MAXFREELIST 0
 #  define PyList_MAXFREELIST 0
 #  define PyFloat_MAXFREELIST 0
 #endif
@@ -24,6 +32,23 @@ struct _Py_list_state {
 #endif
 };
 
+struct _Py_tuple_state {
+#if WITH_FREELISTS
+    /* There is one freelist for each size from 1 to PyTuple_MAXSAVESIZE.
+       The empty tuple is handled separately.
+
+       Each tuple stored in the array is the head of the linked list
+       (and the next available tuple) for that size.  The actual tuple
+       object is used as the linked list node, with its first item
+       (ob_item[0]) pointing to the next node (i.e. the previous head).
+       Each linked list is initially NULL. */
+    PyTupleObject *free_list[PyTuple_NFREELISTS];
+    int numfree[PyTuple_NFREELISTS];
+#else
+    char _unused;  // Empty structs are not allowed.
+#endif
+};
+
 struct _Py_float_state {
 #ifdef WITH_FREELISTS
     /* Special free list
@@ -36,6 +61,7 @@ struct _Py_float_state {
 
 typedef struct _Py_freelist_state {
     struct _Py_float_state float_state;
+    struct _Py_tuple_state tuple_state;
     struct _Py_list_state list_state;
 } _PyFreeListState;
 
index 753763a5a502207d0ce36ff9c5d474ab7a61afa7..c029b23930664869049b29ca3236051717da5112 100644 (file)
@@ -246,7 +246,7 @@ extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs);
 // Functions to clear types free lists
 extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp);
 extern void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization);
-extern void _PyTuple_ClearFreeList(PyInterpreterState *interp);
+extern void _PyTuple_ClearFreeList(_PyFreeListState *state, int is_finalization);
 extern void _PyFloat_ClearFreeList(_PyFreeListState *state, int is_finalization);
 extern void _PyList_ClearFreeList(_PyFreeListState *state, int is_finalization);
 extern void _PyDict_ClearFreeList(PyInterpreterState *interp);
index 4fa7a12206bcb21bc611ed77c2789f5a18000210..b348339a505b0f73817cfb705afbf94513a56cf5 100644 (file)
@@ -14,59 +14,16 @@ extern void _PyTuple_DebugMallocStats(FILE *out);
 /* runtime lifecycle */
 
 extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);
-extern void _PyTuple_Fini(PyInterpreterState *);
+extern void _PyTuple_Fini(_PyFreeListState *);
 
 
 /* other API */
 
-// PyTuple_MAXSAVESIZE - largest tuple to save on free list
-// PyTuple_MAXFREELIST - maximum number of tuples of each size to save
-
-#if defined(PyTuple_MAXSAVESIZE) && PyTuple_MAXSAVESIZE <= 0
-   // A build indicated that tuple freelists should not be used.
-#  define PyTuple_NFREELISTS 0
-#  undef PyTuple_MAXSAVESIZE
-#  undef PyTuple_MAXFREELIST
-
-#elif !defined(WITH_FREELISTS)
-#  define PyTuple_NFREELISTS 0
-#  undef PyTuple_MAXSAVESIZE
-#  undef PyTuple_MAXFREELIST
-
-#else
-   // We are using a freelist for tuples.
-#  ifndef PyTuple_MAXSAVESIZE
-#    define PyTuple_MAXSAVESIZE 20
-#  endif
-#  define PyTuple_NFREELISTS PyTuple_MAXSAVESIZE
-#  ifndef PyTuple_MAXFREELIST
-#    define PyTuple_MAXFREELIST 2000
-#  endif
-#endif
-
-struct _Py_tuple_state {
-#if PyTuple_NFREELISTS > 0
-    /* There is one freelist for each size from 1 to PyTuple_MAXSAVESIZE.
-       The empty tuple is handled separately.
-
-       Each tuple stored in the array is the head of the linked list
-       (and the next available tuple) for that size.  The actual tuple
-       object is used as the linked list node, with its first item
-       (ob_item[0]) pointing to the next node (i.e. the previous head).
-       Each linked list is initially NULL. */
-    PyTupleObject *free_list[PyTuple_NFREELISTS];
-    int numfree[PyTuple_NFREELISTS];
-#else
-    char _unused;  // Empty structs are not allowed.
-#endif
-};
-
 #define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item)
 
 extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
 extern PyObject *_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);
 
-
 typedef struct {
     PyObject_HEAD
     Py_ssize_t it_index;
index d567839c5e3a0ba46ae1d44b197da463e825e1d5..e1b8e4004c616386619eabac2bcd366661423aef 100644 (file)
@@ -962,18 +962,18 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
 }
 
 
-static void maybe_freelist_clear(PyInterpreterState *, int);
+static void maybe_freelist_clear(_PyFreeListState *, int);
 
 void
-_PyTuple_Fini(PyInterpreterState *interp)
+_PyTuple_Fini(_PyFreeListState *state)
 {
-    maybe_freelist_clear(interp, 1);
+    maybe_freelist_clear(state, 1);
 }
 
 void
-_PyTuple_ClearFreeList(PyInterpreterState *interp)
+_PyTuple_ClearFreeList(_PyFreeListState *state, int is_finalization)
 {
-    maybe_freelist_clear(interp, 0);
+    maybe_freelist_clear(state, is_finalization);
 }
 
 /*********************** Tuple Iterator **************************/
@@ -1125,18 +1125,14 @@ tuple_iter(PyObject *seq)
  * freelists *
  *************/
 
-#define STATE (interp->tuple)
+#define STATE (state->tuple_state)
 #define FREELIST_FINALIZED (STATE.numfree[0] < 0)
 
 static inline PyTupleObject *
 maybe_freelist_pop(Py_ssize_t size)
 {
-#if PyTuple_NFREELISTS > 0
-    PyInterpreterState *interp = _PyInterpreterState_GET();
-#ifdef Py_DEBUG
-    /* maybe_freelist_pop() must not be called after maybe_freelist_fini(). */
-    assert(!FREELIST_FINALIZED);
-#endif
+#ifdef WITH_FREELISTS
+    _PyFreeListState *state = _PyFreeListState_GET();
     if (size == 0) {
         return NULL;
     }
@@ -1169,18 +1165,15 @@ maybe_freelist_pop(Py_ssize_t size)
 static inline int
 maybe_freelist_push(PyTupleObject *op)
 {
-#if PyTuple_NFREELISTS > 0
-    PyInterpreterState *interp = _PyInterpreterState_GET();
-#ifdef Py_DEBUG
-    /* maybe_freelist_push() must not be called after maybe_freelist_fini(). */
-    assert(!FREELIST_FINALIZED);
-#endif
+#ifdef WITH_FREELISTS
+    _PyFreeListState *state = _PyFreeListState_GET();
     if (Py_SIZE(op) == 0) {
         return 0;
     }
     Py_ssize_t index = Py_SIZE(op) - 1;
     if (index < PyTuple_NFREELISTS
         && STATE.numfree[index] < PyTuple_MAXFREELIST
+        && STATE.numfree[index] >= 0
         && Py_IS_TYPE(op, &PyTuple_Type))
     {
         /* op is the head of a linked list, with the first item
@@ -1196,9 +1189,9 @@ maybe_freelist_push(PyTupleObject *op)
 }
 
 static void
-maybe_freelist_clear(PyInterpreterState *interp, int fini)
+maybe_freelist_clear(_PyFreeListState *state, int fini)
 {
-#if PyTuple_NFREELISTS > 0
+#ifdef WITH_FREELISTS
     for (Py_ssize_t i = 0; i < PyTuple_NFREELISTS; i++) {
         PyTupleObject *p = STATE.free_list[i];
         STATE.free_list[i] = NULL;
@@ -1216,8 +1209,8 @@ maybe_freelist_clear(PyInterpreterState *interp, int fini)
 void
 _PyTuple_DebugMallocStats(FILE *out)
 {
-#if PyTuple_NFREELISTS > 0
-    PyInterpreterState *interp = _PyInterpreterState_GET();
+#ifdef WITH_FREELISTS
+    _PyFreeListState *state = _PyFreeListState_GET();
     for (int i = 0; i < PyTuple_NFREELISTS; i++) {
         int len = i + 1;
         char buf[128];
index c19893a39c161396f199acd88b3adacb2e10abc8..b1d88ff84a9a9e3b75dc794c1f66803e6364deef 100644 (file)
@@ -14,7 +14,6 @@
 void
 _PyGC_ClearAllFreeLists(PyInterpreterState *interp)
 {
-    _PyTuple_ClearFreeList(interp);
     _PyDict_ClearFreeList(interp);
     _PyAsyncGen_ClearFreeLists(interp);
     _PyContext_ClearFreeList(interp);
index c8ca397be7ae585bc0e5e474e07f4e8f35eafac5..873fad8a3746aa13434059d824f7a4f3df69ff51 100644 (file)
@@ -11,7 +11,6 @@
 void
 _PyGC_ClearAllFreeLists(PyInterpreterState *interp)
 {
-    _PyTuple_ClearFreeList(interp);
     _PyDict_ClearFreeList(interp);
     _PyAsyncGen_ClearFreeLists(interp);
     _PyContext_ClearFreeList(interp);
index 6468e72eaad5640e27bd400d16cf00d6a43c476c..4198f6a38f0e56cfdbf65ae7b86e24f818f6cb5b 100644 (file)
@@ -1752,13 +1752,13 @@ finalize_interp_types(PyInterpreterState *interp)
     _PyUnicode_ClearInterned(interp);
 
     _PyDict_Fini(interp);
-    _PyTuple_Fini(interp);
 
     _PySlice_Fini(interp);
 
     _PyUnicode_Fini(interp);
 
     _PyFreeListState *state = _PyFreeListState_GET();
+    _PyTuple_Fini(state);
     _PyList_Fini(state);
     _PyFloat_Fini(state);
 
index 683e29277b4675b49ad2caa3f5e38d3fea9068a2..eaf77b0da62a843e2aca6b8b91cd22ea13482863 100644 (file)
@@ -1459,6 +1459,7 @@ void
 _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization)
 {
     _PyFloat_ClearFreeList(state, is_finalization);
+    _PyTuple_ClearFreeList(state, is_finalization);
     _PyList_ClearFreeList(state, is_finalization);
 }