]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-126703: Add freelists for list and tuple iterators (GH-128592)
authorPieter Eendebak <pieter.eendebak@gmail.com>
Wed, 29 Jan 2025 09:15:24 +0000 (10:15 +0100)
committerGitHub <noreply@github.com>
Wed, 29 Jan 2025 09:15:24 +0000 (09:15 +0000)
Include/internal/pycore_freelist_state.h
Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-26-40.gh-issue-126703.9i-S5t.rst [new file with mode: 0644]
Objects/listobject.c
Objects/object.c
Objects/tupleobject.c

index 2ccd1ac055b7472b64b9f7826375901528cf75da..7c252f5b570c13b17e33ce0ee9ddcb27eec42e3a 100644 (file)
@@ -11,6 +11,8 @@ extern "C" {
 #  define PyTuple_MAXSAVESIZE 20     // Largest tuple to save on freelist
 #  define Py_tuple_MAXFREELIST 2000  // Maximum number of tuples of each size to save
 #  define Py_lists_MAXFREELIST 80
+#  define Py_list_iters_MAXFREELIST 10
+#  define Py_tuple_iters_MAXFREELIST 10
 #  define Py_dicts_MAXFREELIST 80
 #  define Py_dictkeys_MAXFREELIST 80
 #  define Py_floats_MAXFREELIST 100
@@ -40,6 +42,8 @@ struct _Py_freelists {
     struct _Py_freelist ints;
     struct _Py_freelist tuples[PyTuple_MAXSAVESIZE];
     struct _Py_freelist lists;
+    struct _Py_freelist list_iters;
+    struct _Py_freelist tuple_iters;
     struct _Py_freelist dicts;
     struct _Py_freelist dictkeys;
     struct _Py_freelist slices;
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-26-40.gh-issue-126703.9i-S5t.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-26-40.gh-issue-126703.9i-S5t.rst
new file mode 100644 (file)
index 0000000..dcd5f44
--- /dev/null
@@ -0,0 +1 @@
+Improve performance of iterating over lists and tuples by using a freelist for the iterator objects.
index 099e65c0c25feddf9cf8c31498193a085f706b79..f4a269e4d7b2849fbadd7d8a56003f3c14f86996 100644 (file)
@@ -3903,15 +3903,17 @@ PyTypeObject PyListIter_Type = {
 static PyObject *
 list_iter(PyObject *seq)
 {
-    _PyListIterObject *it;
-
     if (!PyList_Check(seq)) {
         PyErr_BadInternalCall();
         return NULL;
     }
-    it = PyObject_GC_New(_PyListIterObject, &PyListIter_Type);
-    if (it == NULL)
-        return NULL;
+    _PyListIterObject *it = _Py_FREELIST_POP(_PyListIterObject, list_iters);
+    if (it == NULL) {
+        it = PyObject_GC_New(_PyListIterObject, &PyListIter_Type);
+        if (it == NULL) {
+            return NULL;
+        }
+    }
     it->it_index = 0;
     it->it_seq = (PyListObject *)Py_NewRef(seq);
     _PyObject_GC_TRACK(it);
@@ -3924,7 +3926,8 @@ listiter_dealloc(PyObject *self)
     _PyListIterObject *it = (_PyListIterObject *)self;
     _PyObject_GC_UNTRACK(it);
     Py_XDECREF(it->it_seq);
-    PyObject_GC_Del(it);
+    assert(Py_IS_TYPE(self, &PyListIter_Type));
+    _Py_FREELIST_FREE(list_iters, it, PyObject_GC_Del);
 }
 
 static int
index cd48d2f75ba490819d0ee40464fe57878ad66fa2..fdff16138201a0f7ef687e386625638cd887ce99 100644 (file)
@@ -923,6 +923,8 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
         clear_freelist(&freelists->tuples[i], is_finalization, free_object);
     }
     clear_freelist(&freelists->lists, is_finalization, free_object);
+    clear_freelist(&freelists->list_iters, is_finalization, free_object);
+    clear_freelist(&freelists->tuple_iters, is_finalization, free_object);
     clear_freelist(&freelists->dicts, is_finalization, free_object);
     clear_freelist(&freelists->dictkeys, is_finalization, PyMem_Free);
     clear_freelist(&freelists->slices, is_finalization, free_object);
index 7fe8553030a02e08d75e4cafe5a048709212630c..60af9e40e3fe8386e2b830dc7264f1c79eb7ab67 100644 (file)
@@ -993,7 +993,8 @@ tupleiter_dealloc(PyObject *self)
     _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
     _PyObject_GC_UNTRACK(it);
     Py_XDECREF(it->it_seq);
-    PyObject_GC_Del(it);
+    assert(Py_IS_TYPE(self, &PyTupleIter_Type));
+    _Py_FREELIST_FREE(tuple_iters, it, PyObject_GC_Del);
 }
 
 static int
@@ -1119,15 +1120,16 @@ PyTypeObject PyTupleIter_Type = {
 static PyObject *
 tuple_iter(PyObject *seq)
 {
-    _PyTupleIterObject *it;
-
     if (!PyTuple_Check(seq)) {
         PyErr_BadInternalCall();
         return NULL;
     }
-    it = PyObject_GC_New(_PyTupleIterObject, &PyTupleIter_Type);
-    if (it == NULL)
-        return NULL;
+    _PyTupleIterObject *it = _Py_FREELIST_POP(_PyTupleIterObject, tuple_iters);
+    if (it == NULL) {
+        it = PyObject_GC_New(_PyTupleIterObject, &PyTupleIter_Type);
+        if (it == NULL)
+            return NULL;
+    }
     it->it_index = 0;
     it->it_seq = (PyTupleObject *)Py_NewRef(seq);
     _PyObject_GC_TRACK(it);