Skip to content
Open
3 changes: 2 additions & 1 deletion Include/internal/pycore_freelist_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Increase usage of freelist for :class:`int` allocation.
45 changes: 35 additions & 10 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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) +
Expand Down Expand Up @@ -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]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ints[1] is used here?
Maybe some comment on it will be useful?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we need only ndigits==1 integers.

New implementation uses several free lists (though, ints[0] isn't used).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ints[0] is indeed not used. It takes a tiny bit of extra memory, but makes the code cleaner.

if (v == NULL) {
v = PyObject_Malloc(sizeof(PyLongObject));
if (v == NULL) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it equivalent to run PyObject_Free(self) and to run Py_TYPE(self)->tp_free(self) for integers with PyLong_MAXSAVESIZE or more digits?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is for exact ints (but might not be for subclasses).

}
return;
}

Py_TYPE(self)->tp_free(self);
}

Expand Down
3 changes: 3 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,9 @@
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);
Expand All @@ -951,7 +954,7 @@
}
clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free);
clear_freelist(&freelists->bytes_writers, is_finalization, PyMem_Free);
clear_freelist(&freelists->ints, is_finalization, free_object);

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, tail-call)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, tail-call)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, tail-call)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, tail-call)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, tail-call)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, tail-call)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, tail-call)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, tail-call)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [C:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64, switch-case)

'clear_freelist': different types for formal and actual parameter 1 [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64, switch-case)

'function': '_Py_freelist *' differs in levels of indirection from '_Py_freelist (*)[6]' [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04)

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04)

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Ubuntu (bolt) / build and test (ubuntu-24.04)

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Ubuntu (free-threading) / build and test (ubuntu-24.04-arm)

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 957 in Objects/object.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test (ubuntu-24.04-arm)

passing argument 1 of ‘clear_freelist’ from incompatible pointer type [-Wincompatible-pointer-types]
clear_freelist(&freelists->pycfunctionobject, is_finalization, PyObject_GC_Del);
clear_freelist(&freelists->pycmethodobject, is_finalization, PyObject_GC_Del);
clear_freelist(&freelists->pymethodobjects, is_finalization, free_object);
Expand Down
Loading