Skip to content

Commit dff8bcf

Browse files
authored
gh-126703: Add freelist for range and range_iter objects (GH-128619)
1 parent 2067378 commit dff8bcf

File tree

4 files changed

+30
-9
lines changed

4 files changed

+30
-9
lines changed

Include/internal/pycore_freelist_state.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ extern "C" {
1818
# define Py_floats_MAXFREELIST 100
1919
# define Py_ints_MAXFREELIST 100
2020
# define Py_slices_MAXFREELIST 1
21+
# define Py_ranges_MAXFREELIST 6
22+
# define Py_range_iters_MAXFREELIST 6
2123
# define Py_contexts_MAXFREELIST 255
2224
# define Py_async_gens_MAXFREELIST 80
2325
# define Py_async_gen_asends_MAXFREELIST 80
@@ -49,6 +51,8 @@ struct _Py_freelists {
4951
struct _Py_freelist dicts;
5052
struct _Py_freelist dictkeys;
5153
struct _Py_freelist slices;
54+
struct _Py_freelist ranges;
55+
struct _Py_freelist range_iters;
5256
struct _Py_freelist contexts;
5357
struct _Py_freelist async_gens;
5458
struct _Py_freelist async_gen_asends;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve performance of :class:`range` by using a freelist.

Objects/object.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,8 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
931931
clear_freelist(&freelists->dicts, is_finalization, free_object);
932932
clear_freelist(&freelists->dictkeys, is_finalization, PyMem_Free);
933933
clear_freelist(&freelists->slices, is_finalization, free_object);
934+
clear_freelist(&freelists->ranges, is_finalization, free_object);
935+
clear_freelist(&freelists->range_iters, is_finalization, free_object);
934936
clear_freelist(&freelists->contexts, is_finalization, free_object);
935937
clear_freelist(&freelists->async_gens, is_finalization, free_object);
936938
clear_freelist(&freelists->async_gen_asends, is_finalization, free_object);

Objects/rangeobject.c

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Python.h"
44
#include "pycore_abstract.h" // _PyIndex_Check()
55
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
6+
#include "pycore_freelist.h"
67
#include "pycore_long.h" // _PyLong_GetZero()
78
#include "pycore_modsupport.h" // _PyArg_NoKwnames()
89
#include "pycore_range.h"
@@ -51,16 +52,18 @@ static rangeobject *
5152
make_range_object(PyTypeObject *type, PyObject *start,
5253
PyObject *stop, PyObject *step)
5354
{
54-
rangeobject *obj = NULL;
5555
PyObject *length;
5656
length = compute_range_length(start, stop, step);
5757
if (length == NULL) {
5858
return NULL;
5959
}
60-
obj = PyObject_New(rangeobject, type);
60+
rangeobject *obj = _Py_FREELIST_POP(rangeobject, ranges);
6161
if (obj == NULL) {
62-
Py_DECREF(length);
63-
return NULL;
62+
obj = PyObject_New(rangeobject, type);
63+
if (obj == NULL) {
64+
Py_DECREF(length);
65+
return NULL;
66+
}
6467
}
6568
obj->start = start;
6669
obj->stop = stop;
@@ -171,7 +174,7 @@ range_dealloc(PyObject *op)
171174
Py_DECREF(r->stop);
172175
Py_DECREF(r->step);
173176
Py_DECREF(r->length);
174-
PyObject_Free(r);
177+
_Py_FREELIST_FREE(ranges, r, PyObject_Free);
175178
}
176179

177180
static unsigned long
@@ -895,6 +898,12 @@ rangeiter_setstate(PyObject *op, PyObject *state)
895898
Py_RETURN_NONE;
896899
}
897900

901+
static void
902+
rangeiter_dealloc(PyObject *self)
903+
{
904+
_Py_FREELIST_FREE(range_iters, (_PyRangeIterObject *)self, PyObject_Free);
905+
}
906+
898907
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
899908
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
900909

@@ -911,7 +920,7 @@ PyTypeObject PyRangeIter_Type = {
911920
sizeof(_PyRangeIterObject), /* tp_basicsize */
912921
0, /* tp_itemsize */
913922
/* methods */
914-
0, /* tp_dealloc */
923+
rangeiter_dealloc, /* tp_dealloc */
915924
0, /* tp_vectorcall_offset */
916925
0, /* tp_getattr */
917926
0, /* tp_setattr */
@@ -972,9 +981,14 @@ get_len_of_range(long lo, long hi, long step)
972981
static PyObject *
973982
fast_range_iter(long start, long stop, long step, long len)
974983
{
975-
_PyRangeIterObject *it = PyObject_New(_PyRangeIterObject, &PyRangeIter_Type);
976-
if (it == NULL)
977-
return NULL;
984+
_PyRangeIterObject *it = _Py_FREELIST_POP(_PyRangeIterObject, range_iters);
985+
if (it == NULL) {
986+
it = PyObject_New(_PyRangeIterObject, &PyRangeIter_Type);
987+
if (it == NULL) {
988+
return NULL;
989+
}
990+
}
991+
assert(Py_IS_TYPE(it, &PyRangeIter_Type));
978992
it->start = start;
979993
it->step = step;
980994
it->len = len;

0 commit comments

Comments
 (0)