gh-158592: Skip reallocation when shrinking a small list (gh-158787)

This commit is contained in:
Donghee Na authored and GitHub committed 2026-10-08 08:58:23 +09:00
1 parent 12fbc90e06
commit cdfdf7bb73
2 files changed
+26 -3

No files matched your search

@@ -0,0 +1,2 @@
Skip the array reallocation when shrinking a small :class:`list`. Patch by
Donghee Na.
+24 -3
View File
@@ -91,6 +91,10 @@ ensure_shared_on_resize(PyListObject *self)
#endif
}
#define LIST_SMALL_ALLOCATED 32
static int py_list_resize(PyListObject *self, Py_ssize_t newsize);
/* 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
@@ -99,22 +103,37 @@ ensure_shared_on_resize(PyListObject *self)
* Failure is impossible if newsize <= self.allocated on entry.
* Note that self->ob_item may change, and even if newsize is less
* than ob_size on entry.
*
* Always inlining list_resize() makes the fast path a few instructions
* in each caller instead of a function call.
*/
static int
static inline Py_ALWAYS_INLINE int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
size_t new_allocated, target_bytes;
Py_ssize_t allocated = self->allocated;
/* Bypass realloc() when a previous overallocation is large enough
to accommodate the newsize. If the newsize falls lower than half
the allocated size, then proceed with the realloc() to shrink the list.
gh-158592: do not shrink a small list, the realloc() cost is bigger
than the memory we get back.
*/
if (allocated >= newsize && newsize >= (allocated >> 1)) {
if (allocated >= newsize
&& (newsize >= (allocated >> 1) || allocated <= LIST_SMALL_ALLOCATED))
{
assert(self->ob_item != NULL || newsize == 0);
Py_SET_SIZE(self, newsize);
return 0;
}
return py_list_resize(self, newsize);
}
/* Slow path of list_resize(): allocate or reallocate ob_item. */
static int
py_list_resize(PyListObject *self, Py_ssize_t newsize)
{
size_t new_allocated, target_bytes;
Py_ssize_t allocated = self->allocated;
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
@@ -136,6 +155,8 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
if (newsize == 0)
new_allocated = 0;
assert(newsize > allocated || new_allocated < (size_t)allocated);
ensure_shared_on_resize(self);
#ifdef Py_GIL_DISABLED