Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Include/internal/pycore_freelist_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Increase usage of freelist for :class:`int` allocation.
35 changes: 26 additions & 9 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,21 @@ 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);

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 @@ -167,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) +
Expand Down Expand Up @@ -248,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]);
Copy link
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
Contributor

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
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 @@ -3636,11 +3651,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 @@ -3656,10 +3670,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
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
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
4 changes: 3 additions & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,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);
Expand All @@ -941,7 +944,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);
}

Expand Down
Loading