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