Skip to content

Commit a3277e4

Browse files
committed
perf(longobject): extend freelist to 3-digit PyLong objects
Extends the ints2 freelist pattern to 3-digit objects, which cover the range [2^60, 2^63-1] (positive) and [-2^63, -2^60] (negative) on 30-bit builds - including INT64_MAX, INT64_MIN, and nanosecond-precision timestamps. Also fuses the two _PyLong_IsCompact + _PyLong_DigitCount checks in long_dealloc under a single PyLong_CheckExact branch. Benchmark (5M ops, 30-bit build): 2-digit+2-digit -> 3-digit result: 19.6 ns -> 17.0 ns (-13%) 3-digit+compact -> 3-digit result: 18.3 ns -> 15.4 ns (-16%) INT64_MAX + 0: 18.2 ns -> 15.9 ns (-13%) INT64_MIN + 0: 18.1 ns -> 16.2 ns (-10%)
1 parent 87758ec commit a3277e4

3 files changed

Lines changed: 26 additions & 8 deletions

File tree

Include/internal/pycore_freelist_state.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern "C" {
1919
# define Py_complexes_MAXFREELIST 100
2020
# define Py_ints_MAXFREELIST 100
2121
# define Py_ints2_MAXFREELIST 100
22+
# define Py_ints3_MAXFREELIST 50
2223
# define Py_slices_MAXFREELIST 1
2324
# define Py_ranges_MAXFREELIST 6
2425
# define Py_range_iters_MAXFREELIST 6
@@ -49,6 +50,7 @@ struct _Py_freelists {
4950
struct _Py_freelist complexes;
5051
struct _Py_freelist ints;
5152
struct _Py_freelist ints2;
53+
struct _Py_freelist ints3;
5254
struct _Py_freelist tuples[PyTuple_MAXSAVESIZE];
5355
struct _Py_freelist lists;
5456
struct _Py_freelist list_iters;

Objects/longobject.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ long_alloc(Py_ssize_t size)
175175
else if (ndigits == 2) {
176176
result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints2);
177177
}
178+
else if (ndigits == 3) {
179+
result = (PyLongObject *)_Py_FREELIST_POP(PyLongObject, ints3);
180+
}
178181
if (result == NULL) {
179182
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
180183
sizeof(digit)*size. Previous incarnations of this code used
@@ -3647,10 +3650,15 @@ _PyLong_ExactDealloc(PyObject *self)
36473650
_Py_FREELIST_FREE(ints, self, PyObject_Free);
36483651
return;
36493652
}
3650-
if (_PyLong_DigitCount((PyLongObject *)self) == 2) {
3653+
Py_ssize_t ndigits = _PyLong_DigitCount((PyLongObject *)self);
3654+
if (ndigits == 2) {
36513655
_Py_FREELIST_FREE(ints2, self, PyObject_Free);
36523656
return;
36533657
}
3658+
if (ndigits == 3) {
3659+
_Py_FREELIST_FREE(ints3, self, PyObject_Free);
3660+
return;
3661+
}
36543662
PyObject_Free(self);
36553663
}
36563664

@@ -3667,13 +3675,20 @@ long_dealloc(PyObject *self)
36673675
_Py_SetImmortal(self);
36683676
return;
36693677
}
3670-
if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) {
3671-
_Py_FREELIST_FREE(ints, self, PyObject_Free);
3672-
return;
3673-
}
3674-
if (PyLong_CheckExact(self) && _PyLong_DigitCount((PyLongObject *)self) == 2) {
3675-
_Py_FREELIST_FREE(ints2, self, PyObject_Free);
3676-
return;
3678+
if (PyLong_CheckExact(self)) {
3679+
if (_PyLong_IsCompact((PyLongObject *)self)) {
3680+
_Py_FREELIST_FREE(ints, self, PyObject_Free);
3681+
return;
3682+
}
3683+
Py_ssize_t ndigits = _PyLong_DigitCount((PyLongObject *)self);
3684+
if (ndigits == 2) {
3685+
_Py_FREELIST_FREE(ints2, self, PyObject_Free);
3686+
return;
3687+
}
3688+
if (ndigits == 3) {
3689+
_Py_FREELIST_FREE(ints3, self, PyObject_Free);
3690+
return;
3691+
}
36773692
}
36783693
Py_TYPE(self)->tp_free(self);
36793694
}

Objects/object.c

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

0 commit comments

Comments
 (0)