diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index b3abe2030a03da..134607b31d99a8 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -198,6 +198,7 @@ extern Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate);
// Functions to clear types free lists
extern void _PyTuple_ClearFreeList(PyInterpreterState *interp);
extern void _PyFloat_ClearFreeList(PyInterpreterState *interp);
+extern void _PyLong_ClearFreeList(PyInterpreterState *interp);
extern void _PyList_ClearFreeList(PyInterpreterState *interp);
extern void _PyDict_ClearFreeList(PyInterpreterState *interp);
extern void _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp);
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 532b28499080f2..d26e8c00547003 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -21,6 +21,7 @@ extern "C" {
#include "pycore_genobject.h" // struct _Py_async_gen_state
#include "pycore_gc.h" // struct _gc_runtime_state
#include "pycore_list.h" // struct _Py_list_state
+#include "pycore_longobject.h" // struct _Py_long_state
#include "pycore_global_objects.h" // struct _Py_interp_static_objects
#include "pycore_tuple.h" // struct _Py_tuple_state
#include "pycore_typeobject.h" // struct type_cache
@@ -73,11 +74,6 @@ struct atexit_state {
};
-struct _Py_long_state {
- int max_str_digits;
-};
-
-
/* interpreter state */
/* PyInterpreterState holds the global state for one of the runtime's
diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h
index 30c97b7edc98e1..1e822cc1e416b8 100644
--- a/Include/internal/pycore_long.h
+++ b/Include/internal/pycore_long.h
@@ -51,6 +51,7 @@ extern "C" {
extern PyStatus _PyLong_InitTypes(PyInterpreterState *);
extern void _PyLong_FiniTypes(PyInterpreterState *interp);
+extern void _PyLong_Fini(PyInterpreterState *);
/* other API */
@@ -81,6 +82,7 @@ static inline PyObject* _PyLong_FromUnsignedChar(unsigned char i)
PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Subtract(PyLongObject *left, PyLongObject *right);
+void _PyLong_ExactDealloc(PyObject *obj);
int _PyLong_AssignValue(PyObject **target, Py_ssize_t value);
diff --git a/Include/internal/pycore_longobject.h b/Include/internal/pycore_longobject.h
new file mode 100644
index 00000000000000..d92e5f2f630a44
--- /dev/null
+++ b/Include/internal/pycore_longobject.h
@@ -0,0 +1,30 @@
+#ifndef Py_INTERNAL_LONGOBJECT_H
+#define Py_INTERNAL_LONGOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef WITH_FREELISTS
+// without freelists
+# define PyLong_MAXFREELIST 0
+#endif
+
+#ifndef PyLong_MAXFREELIST
+# define PyLong_MAXFREELIST 100
+#endif
+
+struct _Py_long_state {
+ int max_str_digits;
+#if PyLong_MAXFREELIST > 0
+ /* Special free list
+ free_list is a singly-linked list of available PyLongObjects,
+ linked via abuse of their ob_type members. */
+ int numfree;
+ PyLongObject *free_list;
+#endif
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_LONGOBJECT_H */
diff --git a/Makefile.pre.in b/Makefile.pre.in
index f6df7a620deaed..7d972e06a99cdd 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1649,6 +1649,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_interpreteridobject.h \
$(srcdir)/Include/internal/pycore_list.h \
$(srcdir)/Include/internal/pycore_long.h \
+ $(srcdir)/Include/internal/pycore_longobject.h \
$(srcdir)/Include/internal/pycore_moduleobject.h \
$(srcdir)/Include/internal/pycore_namespace.h \
$(srcdir)/Include/internal/pycore_object.h \
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-28-11-43-47.gh-issue-91912.QsHR-u.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-28-11-43-47.gh-issue-91912.QsHR-u.rst
new file mode 100644
index 00000000000000..40009695310db7
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-11-28-11-43-47.gh-issue-91912.QsHR-u.rst
@@ -0,0 +1 @@
+Add long object free list for medium value
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index cacfad7335634c..1e354a8c66c54f 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -1039,6 +1039,7 @@ clear_freelists(PyInterpreterState *interp)
{
_PyTuple_ClearFreeList(interp);
_PyFloat_ClearFreeList(interp);
+ _PyLong_ClearFreeList(interp);
_PyList_ClearFreeList(interp);
_PyDict_ClearFreeList(interp);
_PyAsyncGen_ClearFreeLists(interp);
diff --git a/Objects/longobject.c b/Objects/longobject.c
index c84b4d3f316d5d..2f35136ab8e192 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -46,7 +46,7 @@ static inline void
_Py_DECREF_INT(PyLongObject *op)
{
assert(PyLong_CheckExact(op));
- _Py_DECREF_SPECIALIZED((PyObject *)op, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED((PyObject *)op, (destructor) _PyLong_ExactDealloc);
}
static inline int
@@ -137,6 +137,15 @@ long_normalize(PyLongObject *v)
return v;
}
+#if PyLong_MAXFREELIST > 0
+static inline struct _Py_long_state *
+get_long_state(void)
+{
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return &interp->long_state;
+}
+#endif
+
/* Allocate a new int object with size digits.
Return NULL and set exception if we run out of memory. */
@@ -160,11 +169,30 @@ _PyLong_New(Py_ssize_t size)
sizeof(PyVarObject) instead of the offsetof, but this risks being
incorrect in the presence of padding between the PyVarObject header
and the digits. */
- result = PyObject_Malloc(offsetof(PyLongObject, ob_digit) +
- ndigits*sizeof(digit));
- if (!result) {
- PyErr_NoMemory();
- return NULL;
+#if PyLong_MAXFREELIST > 0
+ if (ndigits + 1 < 3) {
+ struct _Py_long_state *state = get_long_state();
+ result = state->free_list;
+ if (result != NULL) {
+#ifdef Py_DEBUG
+ assert(state->numfree != -1);
+#endif
+ state->free_list = (PyLongObject *) Py_TYPE(result);
+ state->numfree--;
+ }
+ } else {
+ result = NULL;
+ }
+
+ if (result == NULL)
+#endif
+ {
+ result = PyObject_Malloc(offsetof(PyLongObject, ob_digit) +
+ ndigits * sizeof(digit));
+ if (!result) {
+ PyErr_NoMemory();
+ return NULL;
+ }
}
_PyObject_InitVar((PyVarObject*)result, &PyLong_Type, size);
return result;
@@ -201,11 +229,25 @@ _PyLong_FromMedium(sdigit x)
{
assert(!IS_SMALL_INT(x));
assert(is_medium_int(x));
- /* We could use a freelist here */
- PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject));
- if (v == NULL) {
- PyErr_NoMemory();
- return NULL;
+ PyLongObject *v;
+#if PyLong_MAXFREELIST > 0
+ struct _Py_long_state *state = get_long_state();
+ v = state->free_list;
+ if (v != NULL) {
+#ifdef Py_DEBUG
+ assert(state->numfree != -1);
+#endif
+ state->free_list = (PyLongObject *) Py_TYPE(v);
+ state->numfree--;
+ }
+ else
+#endif
+ {
+ v = PyObject_Malloc(sizeof(PyLongObject));
+ if (v == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
}
Py_ssize_t sign = x < 0 ? -1: 1;
digit abs_x = x < 0 ? -x : x;
@@ -3285,6 +3327,47 @@ PyLong_AsDouble(PyObject *v)
/* Methods */
+void
+_PyLong_ExactDealloc(PyObject *obj)
+{
+ assert(PyLong_CheckExact(obj));
+ PyLongObject *op = (PyLongObject *)obj;
+#if PyLong_MAXFREELIST > 0
+ if (!IS_MEDIUM_VALUE(op)) {
+ PyObject_FREE(op);
+ return;
+ }
+ struct _Py_long_state *state = get_long_state();
+#ifdef Py_DEBUG
+ assert(state->numfree != -1);
+#endif
+ if (state->numfree >= PyLong_MAXFREELIST) {
+ PyObject_FREE(op);
+ return;
+ }
+ state->numfree++;
+ Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
+ state->free_list = op;
+#else
+ PyObject_Del(op);
+#endif
+}
+
+static void
+long_dealloc(PyObject *op)
+{
+ assert(PyLong_Check(op));
+#if PyLong_MAXFREELIST > 0
+ if (PyLong_CheckExact(op)) {
+ _PyLong_ExactDealloc(op);
+ }
+ else
+#endif
+ {
+ Py_TYPE(op)->tp_free(op);
+ }
+}
+
/* if a < b, return a negative number
if a == b, return 0
if a > b, return a positive number */
@@ -6267,7 +6350,7 @@ PyTypeObject PyLong_Type = {
"int", /* tp_name */
offsetof(PyLongObject, ob_digit), /* tp_basicsize */
sizeof(digit), /* tp_itemsize */
- 0, /* tp_dealloc */
+ (destructor)long_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -6393,3 +6476,29 @@ _PyLong_FiniTypes(PyInterpreterState *interp)
_PyStructSequence_FiniType(&Int_InfoType);
}
+
+void
+_PyLong_ClearFreeList(PyInterpreterState *interp)
+{
+#if PyLong_MAXFREELIST > 0
+ struct _Py_long_state *state = &interp->long_state;
+ PyLongObject *num = state->free_list;
+ while (num != NULL) {
+ PyLongObject *next = (PyLongObject *) Py_TYPE(num);
+ PyObject_Free(num);
+ num = next;
+ }
+ state->free_list = NULL;
+ state->numfree = 0;
+#endif
+}
+
+void
+_PyLong_Fini(PyInterpreterState *interp)
+{
+ _PyLong_ClearFreeList(interp);
+#if defined(Py_DEBUG) && PyLong_MAXFREELIST > 0
+ struct _Py_long_state *state = &interp->long_state;
+ state->numfree = -1;
+#endif
+}
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index f62434370cfdf7..84741192c59a34 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -231,6 +231,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index f44a1ad8550a38..5cb85fc011ef42 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -597,6 +597,9 @@
Include\internal
+
+ Include\internal
+
Include\internal
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index a1f910da8ed54a..811089b276b86f 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -211,8 +211,8 @@ dummy_func(
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
STAT_INC(BINARY_OP, hit);
prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
ERROR_IF(prod == NULL, error);
}
@@ -235,8 +235,8 @@ dummy_func(
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
STAT_INC(BINARY_OP, hit);
sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
ERROR_IF(sub == NULL, error);
}
@@ -318,8 +318,8 @@ dummy_func(
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
ERROR_IF(sum == NULL, error);
}
@@ -392,7 +392,7 @@ dummy_func(
res = PyList_GET_ITEM(list, index);
assert(res != NULL);
Py_INCREF(res);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)_PyLong_ExactDealloc);
Py_DECREF(list);
}
@@ -411,7 +411,7 @@ dummy_func(
res = PyTuple_GET_ITEM(tuple, index);
assert(res != NULL);
Py_INCREF(res);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)_PyLong_ExactDealloc);
Py_DECREF(tuple);
}
@@ -518,7 +518,7 @@ dummy_func(
STACK_SHRINK(3);
assert(old_value != NULL);
Py_DECREF(old_value);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)_PyLong_ExactDealloc);
Py_DECREF(list);
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
}
@@ -2129,8 +2129,8 @@ dummy_func(
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
NEXTOPARG();
STACK_SHRINK(2);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
int jump = (1 << (sign + 1)) & when_to_jump_mask;
if (!jump) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index ae8fdd5e99c3dc..5c3f3a022481b6 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -134,8 +134,8 @@
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
STAT_INC(BINARY_OP, hit);
prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
if (prod == NULL) goto pop_2_error;
STACK_SHRINK(1);
POKE(1, prod);
@@ -172,8 +172,8 @@
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
STAT_INC(BINARY_OP, hit);
sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
if (sub == NULL) goto pop_2_error;
STACK_SHRINK(1);
POKE(1, sub);
@@ -281,8 +281,8 @@
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
if (sum == NULL) goto pop_2_error;
STACK_SHRINK(1);
POKE(1, sum);
@@ -376,7 +376,7 @@
res = PyList_GET_ITEM(list, index);
assert(res != NULL);
Py_INCREF(res);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)_PyLong_ExactDealloc);
Py_DECREF(list);
STACK_SHRINK(1);
POKE(1, res);
@@ -402,7 +402,7 @@
res = PyTuple_GET_ITEM(tuple, index);
assert(res != NULL);
Py_INCREF(res);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)_PyLong_ExactDealloc);
Py_DECREF(tuple);
STACK_SHRINK(1);
POKE(1, res);
@@ -529,7 +529,7 @@
STACK_SHRINK(3);
assert(old_value != NULL);
Py_DECREF(old_value);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)_PyLong_ExactDealloc);
Py_DECREF(list);
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
DISPATCH();
@@ -2144,8 +2144,8 @@
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
NEXTOPARG();
STACK_SHRINK(2);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(left, (destructor)_PyLong_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(right, (destructor)_PyLong_ExactDealloc);
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
int jump = (1 << (sign + 1)) & when_to_jump_mask;
if (!jump) {
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 8209132ebc6c27..d8685390dfcd16 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1749,6 +1749,7 @@ finalize_interp_types(PyInterpreterState *interp)
_PyUnicode_Fini(interp);
_PyFloat_Fini(interp);
+ _PyLong_Fini(interp);
#ifdef Py_DEBUG
_PyStaticObjects_CheckRefcnt(interp);
#endif