Skip to content
Closed
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
1 change: 1 addition & 0 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
// Functions to clear types free lists
extern void _PyFrame_ClearFreeList(PyThreadState *tstate);
extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
extern void _PyLong_ClearFreeList(PyThreadState *tstate);
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
extern void _PyList_ClearFreeList(PyThreadState *tstate);
extern void _PyDict_ClearFreeList(PyThreadState *tstate);
Expand Down
8 changes: 7 additions & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ struct _Py_float_state {
PyFloatObject *free_list;
};

struct _Py_long_state {
// Special free list; similar to _Py_float_state but for PyLongObjects.
int numfree;
PyLongObject *free_list;
};

/* Speed optimization to avoid frequent malloc/free of small tuples */
#ifndef PyTuple_MAXSAVESIZE
// Largest tuple to save on free list
Expand Down Expand Up @@ -243,6 +249,7 @@ struct _is {
#endif
struct _Py_bytes_state bytes;
struct _Py_unicode_state unicode;
struct _Py_long_state long_state;
struct _Py_float_state float_state;
/* Using a cache is very effective since typically only a single slice is
created and then deleted again. */
Expand Down Expand Up @@ -290,4 +297,3 @@ PyAPI_FUNC(void) _PyInterpreterState_IDDecref(struct _is *);
}
#endif
#endif /* !Py_INTERNAL_INTERP_H */

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add free list for long objects.
Comment thread
methane marked this conversation as resolved.
Outdated
1 change: 1 addition & 0 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ clear_freelists(PyThreadState *tstate)
{
_PyFrame_ClearFreeList(tstate);
_PyTuple_ClearFreeList(tstate);
_PyLong_ClearFreeList(tstate);
_PyFloat_ClearFreeList(tstate);
_PyList_ClearFreeList(tstate);
_PyDict_ClearFreeList(tstate);
Expand Down
94 changes: 76 additions & 18 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ maybe_small_long(PyLongObject *v)
#define maybe_small_long(val) (val)
#endif

/* Speed optimization to avoid frequent malloc/free of small ints */
#ifndef PyLong_MAXFREELIST
# define PyLong_MAXFREELIST 100
#endif

/* If a freshly-allocated int is already shared, it must
be a small integer, so negating it must go to PyLong_FromLong */
Py_LOCAL_INLINE(void)
Expand Down Expand Up @@ -130,22 +135,34 @@ long_normalize(PyLongObject *v)
PyLongObject *
_PyLong_New(Py_ssize_t size)
{
struct _Py_long_state *state = &_PyInterpreterState_GET()->long_state;
#ifdef Py_DEBUG
// _PyLong_New() must not be called after _PyLong_Fini()
assert(state->numfree != -1);
#endif

PyLongObject *result;
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
sizeof(PyVarObject) instead of the offsetof, but this risks being
incorrect in the presence of padding between the PyVarObject header
and the digits. */
if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
PyErr_SetString(PyExc_OverflowError,
"too many digits in integer");
return NULL;
if (size == 1 && (result = (PyLongObject*)state->free_list) != NULL) {
state->free_list = (PyLongObject *)Py_TYPE(result);
state->numfree--;
}
result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +
size*sizeof(digit));
if (!result) {
PyErr_NoMemory();
return NULL;
else {
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
sizeof(PyVarObject) instead of the offsetof, but this risks being
incorrect in the presence of padding between the PyVarObject header
and the digits. */
if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
PyErr_SetString(PyExc_OverflowError,
"too many digits in integer");
return NULL;
}
result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +
size*sizeof(digit));
if (!result) {
PyErr_NoMemory();
return NULL;
}
}
_PyObject_InitVar((PyVarObject*)result, &PyLong_Type, size);
return result;
Expand Down Expand Up @@ -204,7 +221,7 @@ PyLong_FromLong(long ival)
}

/* Fast path for single-digit ints */
if (!(abs_ival >> PyLong_SHIFT)) {
if (abs_ival < (1UL << PyLong_SHIFT)) {
v = _PyLong_New(1);
if (v) {
Py_SET_SIZE(v, sign);
Expand All @@ -214,9 +231,9 @@ PyLong_FromLong(long ival)
return (PyObject*)v;
}

#if PyLong_SHIFT==15
#if 2*PyLong_SHIFT < 8*PY_LONG_SIZE
/* 2 digits */
if (!(abs_ival >> 2*PyLong_SHIFT)) {
if (abs_ival < (1UL << 2*PyLong_SHIFT)) {
v = _PyLong_New(2);
if (v) {
Py_SET_SIZE(v, 2 * sign);
Expand Down Expand Up @@ -2885,6 +2902,27 @@ PyLong_AsDouble(PyObject *v)
return ldexp(x, (int)exponent);
}

static void
long_dealloc(PyObject *v)
{
if (PyLong_CheckExact(v)) {
struct _Py_long_state *state = &_PyInterpreterState_GET()->long_state;
#ifdef Py_DEBUG
// long_dealloc() must not be called after _PyLong_Fini()
assert(state->numfree != -1);
#endif
Py_ssize_t size = Py_SIZE(v);
if ((size == 1 || size == -1) &&
state->numfree < PyLong_MAXFREELIST) {
Py_SET_TYPE(v, (PyTypeObject *)state->free_list);
state->free_list = (PyLongObject *)v;
state->numfree++;
return;
}
}
Py_TYPE(v)->tp_free(v);
}

/* Methods */

/* if a < b, return a negative number
Expand Down Expand Up @@ -5631,7 +5669,7 @@ PyTypeObject PyLong_Type = {
"int", /* tp_name */
offsetof(PyLongObject, ob_digit), /* tp_basicsize */
sizeof(digit), /* tp_itemsize */
0, /* tp_dealloc */
long_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
Expand Down Expand Up @@ -5751,6 +5789,20 @@ _PyLong_Init(PyThreadState *tstate)
return 1;
}

void
_PyLong_ClearFreeList(PyThreadState *tstate)
{
struct _Py_long_state *state = &tstate->interp->long_state;
PyLongObject *f = state->free_list;
while (f != NULL) {
PyLongObject *next = (PyLongObject*) Py_TYPE(f);
PyObject_FREE(f);
f = next;
}
state->free_list = NULL;
state->numfree = 0;
}

void
_PyLong_Fini(PyThreadState *tstate)
{
Expand All @@ -5764,4 +5816,10 @@ _PyLong_Fini(PyThreadState *tstate)
Py_CLEAR(tstate->interp->small_ints[i]);
}
#endif

_PyLong_ClearFreeList(tstate);
#ifdef Py_DEBUG
struct _Py_long_state *state = &tstate->interp->long_state;
state->numfree = -1;
#endif
}