From f180a598844f8f247bd5fbd099e9f23dff93b2bc Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 3 Jan 2025 22:44:09 +0100 Subject: [PATCH 1/4] Add freelists for list and tuple iterators --- Include/internal/pycore_freelist_state.h | 4 ++++ Objects/listobject.c | 14 ++++++++------ Objects/object.c | 2 ++ Objects/tupleobject.c | 13 +++++++------ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_freelist_state.h b/Include/internal/pycore_freelist_state.h index a1a94c1f2dc880..081d9d76108e63 100644 --- a/Include/internal/pycore_freelist_state.h +++ b/Include/internal/pycore_freelist_state.h @@ -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 @@ -39,6 +41,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/Objects/listobject.c b/Objects/listobject.c index bbd53e7de94a31..bfbe7363c3c0c5 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3906,15 +3906,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); @@ -3927,7 +3929,7 @@ listiter_dealloc(PyObject *self) _PyListIterObject *it = (_PyListIterObject *)self; _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); - PyObject_GC_Del(it); + _Py_FREELIST_FREE(list_iters, it, PyObject_GC_Del); } static int diff --git a/Objects/object.c b/Objects/object.c index 4c30257ca26938..93327d7bf4aad7 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -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); diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 49977726eadca9..e63e4c15fda556 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -994,7 +994,7 @@ tupleiter_dealloc(_PyTupleIterObject *it) { _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); - PyObject_GC_Del(it); + _Py_FREELIST_FREE(tuple_iters, it, PyObject_GC_Del); } static int @@ -1116,15 +1116,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); From 7afbc35653fcc709bb871bb7a9c3c795d31f99a6 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 7 Jan 2025 19:23:12 +0100 Subject: [PATCH 2/4] add asserts --- Objects/listobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/listobject.c b/Objects/listobject.c index bfbe7363c3c0c5..6370ba4fbc9f89 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3929,6 +3929,7 @@ listiter_dealloc(PyObject *self) _PyListIterObject *it = (_PyListIterObject *)self; _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); + assert(Py_TYPE(self)==_PyListIterObject); _Py_FREELIST_FREE(list_iters, it, PyObject_GC_Del); } From 77d923d90ea876fd3b2cc2978035964c5e9d4a4a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 7 Jan 2025 19:29:29 +0100 Subject: [PATCH 3/4] refactor --- Objects/listobject.c | 2 +- Objects/tupleobject.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/listobject.c b/Objects/listobject.c index 6370ba4fbc9f89..e7c1d952579eba 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -3929,7 +3929,7 @@ listiter_dealloc(PyObject *self) _PyListIterObject *it = (_PyListIterObject *)self; _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); - assert(Py_TYPE(self)==_PyListIterObject); + assert(Py_IS_TYPE(self, &PyListIter_Type)); _Py_FREELIST_FREE(list_iters, it, PyObject_GC_Del); } diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 67a8a49b03c97b..f1eb3015625f90 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -996,6 +996,7 @@ tupleiter_dealloc(PyObject *self) _PyTupleIterObject *it = _PyTupleIterObject_CAST(self); _PyObject_GC_UNTRACK(it); Py_XDECREF(it->it_seq); + assert(Py_IS_TYPE(self, &PyTupleIter_Type)); _Py_FREELIST_FREE(tuple_iters, it, PyObject_GC_Del); } From a3aed1c7481a29c1cfcfbeea668864368c4fda48 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:26:44 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-01-07-19-26-40.gh-issue-126703.9i-S5t.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-26-40.gh-issue-126703.9i-S5t.rst 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 index 00000000000000..dcd5f449c98ef3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-07-19-26-40.gh-issue-126703.9i-S5t.rst @@ -0,0 +1 @@ +Improve performance of iterating over lists and tuples by using a freelist for the iterator objects.