Skip to content

Commit 87758ec

Browse files
committed
perf(longobject): add freelist for 2-digit PyLong objects
Non-compact (2-digit) int results previously bypassed the freelist and called PyObject_Malloc directly. Add an `ints2` freelist alongside the existing `ints` (1-digit) freelist. - `long_alloc(2)` checks `ints2` before `PyObject_Malloc` - `_PyLong_ExactDealloc` and `long_dealloc` recycle exact 2-digit ints to `ints2` instead of immediately freeing them - `_PyObject_ClearFreeLists` clears `ints2` the same way as `ints`
1 parent 5b69f64 commit 87758ec

3 files changed

Lines changed: 14 additions & 0 deletions

File tree

Include/internal/pycore_freelist_state.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern "C" {
1818
# define Py_floats_MAXFREELIST 100
1919
# define Py_complexes_MAXFREELIST 100
2020
# define Py_ints_MAXFREELIST 100
21+
# define Py_ints2_MAXFREELIST 100
2122
# define Py_slices_MAXFREELIST 1
2223
# define Py_ranges_MAXFREELIST 6
2324
# define Py_range_iters_MAXFREELIST 6
@@ -47,6 +48,7 @@ struct _Py_freelists {
4748
struct _Py_freelist floats;
4849
struct _Py_freelist complexes;
4950
struct _Py_freelist ints;
51+
struct _Py_freelist ints2;
5052
struct _Py_freelist tuples[PyTuple_MAXSAVESIZE];
5153
struct _Py_freelist lists;
5254
struct _Py_freelist list_iters;

Objects/longobject.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ long_alloc(Py_ssize_t size)
172172
if (ndigits == 1) {
173173
result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints);
174174
}
175+
else if (ndigits == 2) {
176+
result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints2);
177+
}
175178
if (result == NULL) {
176179
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
177180
sizeof(digit)*size. Previous incarnations of this code used
@@ -3644,6 +3647,10 @@ _PyLong_ExactDealloc(PyObject *self)
36443647
_Py_FREELIST_FREE(ints, self, PyObject_Free);
36453648
return;
36463649
}
3650+
if (_PyLong_DigitCount((PyLongObject *)self) == 2) {
3651+
_Py_FREELIST_FREE(ints2, self, PyObject_Free);
3652+
return;
3653+
}
36473654
PyObject_Free(self);
36483655
}
36493656

@@ -3664,6 +3671,10 @@ long_dealloc(PyObject *self)
36643671
_Py_FREELIST_FREE(ints, self, PyObject_Free);
36653672
return;
36663673
}
3674+
if (PyLong_CheckExact(self) && _PyLong_DigitCount((PyLongObject *)self) == 2) {
3675+
_Py_FREELIST_FREE(ints2, self, PyObject_Free);
3676+
return;
3677+
}
36673678
Py_TYPE(self)->tp_free(self);
36683679
}
36693680

Objects/object.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,7 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
952952
clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free);
953953
clear_freelist(&freelists->bytes_writers, is_finalization, PyMem_Free);
954954
clear_freelist(&freelists->ints, is_finalization, free_object);
955+
clear_freelist(&freelists->ints2, is_finalization, free_object);
955956
clear_freelist(&freelists->pycfunctionobject, is_finalization, PyObject_GC_Del);
956957
clear_freelist(&freelists->pycmethodobject, is_finalization, PyObject_GC_Del);
957958
clear_freelist(&freelists->pymethodobjects, is_finalization, free_object);

0 commit comments

Comments
 (0)