]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-126703: Add freelist for range and range_iter objects (GH-128619)
authorPieter Eendebak <pieter.eendebak@gmail.com>
Sun, 6 Apr 2025 20:40:52 +0000 (22:40 +0200)
committerGitHub <noreply@github.com>
Sun, 6 Apr 2025 20:40:52 +0000 (04:40 +0800)
Include/internal/pycore_freelist_state.h
Misc/NEWS.d/next/Core_and_Builtins/2025-01-08-09-41-25.gh-issue-126703.djs9e_.rst [new file with mode: 0644]
Objects/object.c
Objects/rangeobject.c

index 54415b22fd41efe82fdacff1bded8d717756da23..4828dfd948f70ab2257d7acee52ac75b43c87a40 100644 (file)
@@ -18,6 +18,8 @@ extern "C" {
 #  define Py_floats_MAXFREELIST 100
 #  define Py_ints_MAXFREELIST 100
 #  define Py_slices_MAXFREELIST 1
+#  define Py_ranges_MAXFREELIST 6
+#  define Py_range_iters_MAXFREELIST 6
 #  define Py_contexts_MAXFREELIST 255
 #  define Py_async_gens_MAXFREELIST 80
 #  define Py_async_gen_asends_MAXFREELIST 80
@@ -49,6 +51,8 @@ struct _Py_freelists {
     struct _Py_freelist dicts;
     struct _Py_freelist dictkeys;
     struct _Py_freelist slices;
+    struct _Py_freelist ranges;
+    struct _Py_freelist range_iters;
     struct _Py_freelist contexts;
     struct _Py_freelist async_gens;
     struct _Py_freelist async_gen_asends;
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-08-09-41-25.gh-issue-126703.djs9e_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-08-09-41-25.gh-issue-126703.djs9e_.rst
new file mode 100644 (file)
index 0000000..2fb4456
--- /dev/null
@@ -0,0 +1 @@
+Improve performance of  :class:`range` by using a freelist.
index 42ac3a1c2baa7bf22c0aaa9477e3b07c86357c8b..99bb1d9c0bfad5d08888fa1643083a174fcc42fa 100644 (file)
@@ -931,6 +931,8 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
     clear_freelist(&freelists->dicts, is_finalization, free_object);
     clear_freelist(&freelists->dictkeys, is_finalization, PyMem_Free);
     clear_freelist(&freelists->slices, is_finalization, free_object);
+    clear_freelist(&freelists->ranges, is_finalization, free_object);
+    clear_freelist(&freelists->range_iters, is_finalization, free_object);
     clear_freelist(&freelists->contexts, is_finalization, free_object);
     clear_freelist(&freelists->async_gens, is_finalization, free_object);
     clear_freelist(&freelists->async_gen_asends, is_finalization, free_object);
index 24f9ce807fd24e8060c8726ef12a4fc7219b16d7..f8cdfe68a6435e20973eabd98276e859de3d1277 100644 (file)
@@ -3,6 +3,7 @@
 #include "Python.h"
 #include "pycore_abstract.h"      // _PyIndex_Check()
 #include "pycore_ceval.h"         // _PyEval_GetBuiltin()
+#include "pycore_freelist.h"
 #include "pycore_long.h"          // _PyLong_GetZero()
 #include "pycore_modsupport.h"    // _PyArg_NoKwnames()
 #include "pycore_range.h"
@@ -51,16 +52,18 @@ static rangeobject *
 make_range_object(PyTypeObject *type, PyObject *start,
                   PyObject *stop, PyObject *step)
 {
-    rangeobject *obj = NULL;
     PyObject *length;
     length = compute_range_length(start, stop, step);
     if (length == NULL) {
         return NULL;
     }
-    obj = PyObject_New(rangeobject, type);
+    rangeobject *obj = _Py_FREELIST_POP(rangeobject, ranges);
     if (obj == NULL) {
-        Py_DECREF(length);
-        return NULL;
+        obj = PyObject_New(rangeobject, type);
+        if (obj == NULL) {
+            Py_DECREF(length);
+            return NULL;
+        }
     }
     obj->start = start;
     obj->stop = stop;
@@ -171,7 +174,7 @@ range_dealloc(PyObject *op)
     Py_DECREF(r->stop);
     Py_DECREF(r->step);
     Py_DECREF(r->length);
-    PyObject_Free(r);
+    _Py_FREELIST_FREE(ranges, r, PyObject_Free);
 }
 
 static unsigned long
@@ -895,6 +898,12 @@ rangeiter_setstate(PyObject *op, PyObject *state)
     Py_RETURN_NONE;
 }
 
+static void
+rangeiter_dealloc(PyObject *self)
+{
+    _Py_FREELIST_FREE(range_iters, (_PyRangeIterObject *)self, PyObject_Free);
+}
+
 PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
 PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
 
@@ -911,7 +920,7 @@ PyTypeObject PyRangeIter_Type = {
         sizeof(_PyRangeIterObject),             /* tp_basicsize */
         0,                                      /* tp_itemsize */
         /* methods */
-        0,                                      /* tp_dealloc */
+        rangeiter_dealloc,                      /* tp_dealloc */
         0,                                      /* tp_vectorcall_offset */
         0,                                      /* tp_getattr */
         0,                                      /* tp_setattr */
@@ -972,9 +981,14 @@ get_len_of_range(long lo, long hi, long step)
 static PyObject *
 fast_range_iter(long start, long stop, long step, long len)
 {
-    _PyRangeIterObject *it = PyObject_New(_PyRangeIterObject, &PyRangeIter_Type);
-    if (it == NULL)
-        return NULL;
+    _PyRangeIterObject *it = _Py_FREELIST_POP(_PyRangeIterObject, range_iters);
+    if (it == NULL) {
+        it = PyObject_New(_PyRangeIterObject, &PyRangeIter_Type);
+        if (it == NULL) {
+            return NULL;
+        }
+    }
+    assert(Py_IS_TYPE(it, &PyRangeIter_Type));
     it->start = start;
     it->step = step;
     it->len = len;