diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-11-59-33.gh-issue-158592._BpoeY.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-11-59-33.gh-issue-158592._BpoeY.rst new file mode 100644 index 00000000000000..d937a81cc4630a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-11-59-33.gh-issue-158592._BpoeY.rst @@ -0,0 +1,2 @@ +Skip the array reallocation when shrinking a small :class:`list`. Patch by +Donghee Na. diff --git a/Objects/listobject.c b/Objects/listobject.c index 81eb3e1102159b..37e0bfe51fc647 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -91,6 +91,8 @@ ensure_shared_on_resize(PyListObject *self) #endif } +#define LIST_SMALL_ALLOCATED 32 + /* Ensure ob_item has room for at least newsize elements, and set * ob_size to newsize. If newsize > ob_size on entry, the content * of the new slots at exit is undefined heap trash; it's the caller's @@ -136,6 +138,15 @@ list_resize(PyListObject *self, Py_ssize_t newsize) if (newsize == 0) new_allocated = 0; + // gh-158602: when shrinking, do not reallocate the array of a small list. + if (newsize < allocated) { + if (allocated <= LIST_SMALL_ALLOCATED) { + Py_SET_SIZE(self, newsize); + return 0; + } + assert(new_allocated < (size_t)allocated); + } + ensure_shared_on_resize(self); #ifdef Py_GIL_DISABLED @@ -1148,7 +1159,7 @@ list_ass_item_lock_held(PyListObject *a, Py_ssize_t i, PyObject *v) for (Py_ssize_t idx = i; idx < size - 1; idx++) { FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item[idx], a->ob_item[idx + 1]); } - Py_SET_SIZE(a, size - 1); + list_resize(a, size - 1); // NB: shrinking a list can't fail } else { FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item[i], Py_NewRef(v));