diff --git a/Include/internal/pycore_freelist_state.h b/Include/internal/pycore_freelist_state.h index 46e2a82ea03456..74024baf532c5c 100644 --- a/Include/internal/pycore_freelist_state.h +++ b/Include/internal/pycore_freelist_state.h @@ -18,6 +18,7 @@ extern "C" { # define Py_floats_MAXFREELIST 100 # define Py_complexes_MAXFREELIST 100 # define Py_ints_MAXFREELIST 100 +# define PyLong_MAXSAVESIZE 6 // Keep freelists for all ints with less than this number of digits # define Py_slices_MAXFREELIST 1 # define Py_ranges_MAXFREELIST 6 # define Py_range_iters_MAXFREELIST 6 @@ -46,8 +47,8 @@ struct _Py_freelist { struct _Py_freelists { struct _Py_freelist floats; struct _Py_freelist complexes; - 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/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. diff --git a/Objects/longobject.c b/Objects/longobject.c index 6e6011cb19aab5..2536d9f526095e 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -155,6 +155,29 @@ 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)); + + PyLongObject *op = (PyLongObject *)self; + Py_ssize_t ndigits = _PyLong_DigitCount(op); + + /* A value that normalized to zero still owns a buffer of at least one + * digit (long_alloc always allocates max(size, 1) digits), so it can be + * reused as a 1-digit int. Bucketing it at index 0 would strand it: + * long_alloc never requests bucket 0 (it clamps ndigits to >= 1). */ + if (ndigits == 0) { + ndigits = 1; + } + + if (ndigits < PyLong_MAXSAVESIZE) { + return _Py_FREELIST_PUSH(ints[ndigits], self, Py_ints_MAXFREELIST); + } + return 0; +} + + static PyLongObject * long_alloc(Py_ssize_t size) { @@ -169,8 +192,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) + @@ -253,7 +276,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) { @@ -334,7 +357,7 @@ medium_from_stwodigits(stwodigits x) if(!is_medium_int(x)) { return PyStackRef_NULL; } - 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) { @@ -3640,11 +3663,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 @@ -3660,10 +3682,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 e0e26bb50d3653..0bb77fac44b833 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -932,6 +932,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);