https://github.com/python/cpython/commit/dff8bcfa3cb2daf0aa0d3f4717fd77948d3b2b2f
commit: dff8bcfa3cb2daf0aa0d3f4717fd77948d3b2b2f
branch: main
author: Pieter Eendebak <[email protected]>
committer: Fidget-Spinner <[email protected]>
date: 2025-04-07T04:40:52+08:00
summary:
gh-126703: Add freelist for range and range_iter objects (GH-128619)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-01-08-09-41-25.gh-issue-126703.djs9e_.rst
M Include/internal/pycore_freelist_state.h
M Objects/object.c
M Objects/rangeobject.c
diff --git a/Include/internal/pycore_freelist_state.h
b/Include/internal/pycore_freelist_state.h
index 54415b22fd41ef..4828dfd948f70a 100644
--- a/Include/internal/pycore_freelist_state.h
+++ b/Include/internal/pycore_freelist_state.h
@@ -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
index 00000000000000..2fb44568b12168
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-08-09-41-25.gh-issue-126703.djs9e_.rst
@@ -0,0 +1 @@
+Improve performance of :class:`range` by using a freelist.
diff --git a/Objects/object.c b/Objects/object.c
index 42ac3a1c2baa7b..99bb1d9c0bfad5 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -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);
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 24f9ce807fd24e..f8cdfe68a6435e 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -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;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]