https://github.com/python/cpython/commit/e431a7a72312468d853f0e46353193d969017eca
commit: e431a7a72312468d853f0e46353193d969017eca
branch: 3.15
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2026-10-10T09:32:10+09:00
summary:

[3.15] gh-158592: Skip reallocation when shrinking a small list (GH-158787) 
(gh-159001)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-10-02-16-19-35.gh-issue-158592.V6W21J.rst
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-11-59-33.gh-issue-158592._BpoeY.rst
M Objects/listobject.c

diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-02-16-19-35.gh-issue-158592.V6W21J.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-02-16-19-35.gh-issue-158592.V6W21J.rst
new file mode 100644
index 00000000000000..1112ca8e63d314
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-02-16-19-35.gh-issue-158592.V6W21J.rst
@@ -0,0 +1 @@
+``del list[index]`` now triggers shrinking the list's internal array.
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 9f059ec875d7ea..b6ebb2b53fc304 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -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
@@ -1148,7 +1169,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));

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to