From 3ccb908bce47e41f00ecd09232ea4ff35bd51c2f Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 4 Feb 2025 11:49:01 +0100 Subject: [PATCH 1/3] Add freelist for ints with small number of digits --- Include/internal/pycore_freelist_state.h | 3 +- Objects/longobject.c | 36 ++++++++++++++++++------ Objects/object.c | 4 ++- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/Include/internal/pycore_freelist_state.h b/Include/internal/pycore_freelist_state.h index 7c252f5b570c13..a0098ce3b4bb95 100644 --- a/Include/internal/pycore_freelist_state.h +++ b/Include/internal/pycore_freelist_state.h @@ -17,6 +17,7 @@ extern "C" { # define Py_dictkeys_MAXFREELIST 80 # define Py_floats_MAXFREELIST 100 # define Py_ints_MAXFREELIST 100 +# define PyLong_MAXSAVESIZE 8 // Keep freelists for all ints with less than this number of digits # define Py_slices_MAXFREELIST 1 # define Py_contexts_MAXFREELIST 255 # define Py_async_gens_MAXFREELIST 80 @@ -39,8 +40,8 @@ struct _Py_freelist { struct _Py_freelists { struct _Py_freelist floats; - struct _Py_freelist ints; struct _Py_freelist tuples[PyTuple_MAXSAVESIZE]; + struct _Py_freelist ints[PyLong_MAXSAVESIZE]; struct _Py_freelist lists; struct _Py_freelist list_iters; struct _Py_freelist tuple_iters; diff --git a/Objects/longobject.c b/Objects/longobject.c index 370328dcfe8c9a..e7ecb29d6e5828 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -152,6 +152,22 @@ long_normalize(PyLongObject *v) # define MAX_LONG_DIGITS ((INT64_MAX-1) / PyLong_SHIFT) #endif +static inline int +maybe_freelist_push(PyObject *self) +{ + assert(PyLong_CheckExact(self)); + assert(Py_IS_TYPE(self, &PyLong_Type)); + + PyLongObject *op = (PyLongObject *)self; + Py_ssize_t ndigits = _PyLong_DigitCount(op); + + if (ndigits < PyLong_MAXSAVESIZE) { + return _Py_FREELIST_PUSH(ints[ndigits], self, Py_ints_MAXFREELIST); + } + return 0; +} + + static PyLongObject * long_alloc(Py_ssize_t size) { @@ -166,8 +182,8 @@ long_alloc(Py_ssize_t size) * assume that there is always at least one digit present. */ Py_ssize_t ndigits = size ? size : 1; - if (ndigits == 1) { - result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints); + if (ndigits < PyLong_MAXSAVESIZE ) { + result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints[ndigits]); } if (result == NULL) { /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + @@ -247,7 +263,7 @@ _PyLong_FromMedium(sdigit x) assert(!IS_SMALL_INT(x)); assert(is_medium_int(x)); - PyLongObject *v = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints); + PyLongObject *v = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints[1]); if (v == NULL) { v = PyObject_Malloc(sizeof(PyLongObject)); if (v == NULL) { @@ -3669,11 +3685,10 @@ _PyLong_ExactDealloc(PyObject *self) _Py_SetImmortal(self); return; } - if (_PyLong_IsCompact((PyLongObject *)self)) { - _Py_FREELIST_FREE(ints, self, PyObject_Free); - return; + + if (!maybe_freelist_push(self)) { + PyObject_Free(self); } - PyObject_Free(self); } static void @@ -3689,10 +3704,13 @@ long_dealloc(PyObject *self) _Py_SetImmortal(self); return; } - if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) { - _Py_FREELIST_FREE(ints, self, PyObject_Free); + if (PyLong_CheckExact(self)) { + if (!maybe_freelist_push(self)) { + PyObject_Free(self); + } return; } + Py_TYPE(self)->tp_free(self); } diff --git a/Objects/object.c b/Objects/object.c index fdff16138201a0..a8de71ffc2eb6c 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -922,6 +922,9 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization) for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) { clear_freelist(&freelists->tuples[i], is_finalization, free_object); } + for (Py_ssize_t i = 0; i < PyLong_MAXSAVESIZE; i++) { + clear_freelist(&freelists->ints[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); @@ -938,7 +941,6 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization) clear_freelist(&freelists->object_stack_chunks, 1, PyMem_RawFree); } clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free); - clear_freelist(&freelists->ints, is_finalization, free_object); clear_freelist(&freelists->pymethodobjects, is_finalization, free_object); } From b27a21e3be2e1726a631d8b1c9b0800a7b68e9b0 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:53:21 +0000 Subject: [PATCH 2/3] =?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-02-04-10-53-18.gh-issue-126703.RpjKez.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-10-53-18.gh-issue-126703.RpjKez.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-10-53-18.gh-issue-126703.RpjKez.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-10-53-18.gh-issue-126703.RpjKez.rst new file mode 100644 index 00000000000000..ede383deb4ad31 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-04-10-53-18.gh-issue-126703.RpjKez.rst @@ -0,0 +1 @@ +Increase usage of freelist for :class:`int` allocation. From a7cb1b3faaa7a10e4ca8426710eff33a60e18235 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 4 Feb 2025 12:15:38 +0100 Subject: [PATCH 3/3] Update Objects/longobject.c --- Objects/longobject.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index e7ecb29d6e5828..87a279ee1b32c0 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -156,7 +156,6 @@ static inline int maybe_freelist_push(PyObject *self) { assert(PyLong_CheckExact(self)); - assert(Py_IS_TYPE(self, &PyLong_Type)); PyLongObject *op = (PyLongObject *)self; Py_ssize_t ndigits = _PyLong_DigitCount(op);