]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-47009: Streamline list.append for the common case (GH-31864)
authorDennis Sweeney <36520290+sweeneyde@users.noreply.github.com>
Fri, 1 Apr 2022 10:23:42 +0000 (06:23 -0400)
committerGitHub <noreply@github.com>
Fri, 1 Apr 2022 10:23:42 +0000 (11:23 +0100)
Include/internal/pycore_list.h
Misc/NEWS.d/next/Core and Builtins/2022-03-14-09-45-10.bpo-47009.ZI05b5.rst [new file with mode: 0644]
Objects/listobject.c
Python/ceval.c

index 0717a1f9563a2ac9b6da9a4731e2cf858a0127b3..860dce1fd5d3938fcf6f146bb7705eb1e1835940 100644 (file)
@@ -37,6 +37,24 @@ struct _Py_list_state {
 
 #define _PyList_ITEMS(op) (_PyList_CAST(op)->ob_item)
 
+extern int
+_PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem);
+
+static inline int
+_PyList_AppendTakeRef(PyListObject *self, PyObject *newitem)
+{
+    assert(self != NULL && newitem != NULL);
+    assert(PyList_Check(self));
+    Py_ssize_t len = PyList_GET_SIZE(self);
+    Py_ssize_t allocated = self->allocated;
+    assert((size_t)len + 1 < PY_SSIZE_T_MAX);
+    if (allocated > len) {
+        PyList_SET_ITEM(self, len, newitem);
+        Py_SET_SIZE(self, len + 1);
+        return 0;
+    }
+    return _PyList_AppendTakeRefListResize(self, newitem);
+}
 
 #ifdef __cplusplus
 }
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-14-09-45-10.bpo-47009.ZI05b5.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-14-09-45-10.bpo-47009.ZI05b5.rst
new file mode 100644 (file)
index 0000000..0c65c34
--- /dev/null
@@ -0,0 +1 @@
+Improved the performance of :meth:`list.append()` and list comprehensions by optimizing for the common case, where no resize is needed. Patch by Dennis Sweeney.
index d50633d2b3132112b4d79d7caf25f9579478c52c..ccb9b91ba930d7f2e2485d17013b37bb244452ba 100644 (file)
@@ -301,26 +301,27 @@ PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
     return ins1((PyListObject *)op, where, newitem);
 }
 
-static int
-app1(PyListObject *self, PyObject *v)
+/* internal, used by _PyList_AppendTakeRef */
+int
+_PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem)
 {
-    Py_ssize_t n = PyList_GET_SIZE(self);
-
-    assert (v != NULL);
-    assert((size_t)n + 1 < PY_SSIZE_T_MAX);
-    if (list_resize(self, n+1) < 0)
+    Py_ssize_t len = PyList_GET_SIZE(self);
+    assert(self->allocated == -1 || self->allocated == len);
+    if (list_resize(self, len + 1) < 0) {
+        Py_DECREF(newitem);
         return -1;
-
-    Py_INCREF(v);
-    PyList_SET_ITEM(self, n, v);
+    }
+    PyList_SET_ITEM(self, len, newitem);
     return 0;
 }
 
 int
 PyList_Append(PyObject *op, PyObject *newitem)
 {
-    if (PyList_Check(op) && (newitem != NULL))
-        return app1((PyListObject *)op, newitem);
+    if (PyList_Check(op) && (newitem != NULL)) {
+        Py_INCREF(newitem);
+        return _PyList_AppendTakeRef((PyListObject *)op, newitem);
+    }
     PyErr_BadInternalCall();
     return -1;
 }
@@ -844,9 +845,10 @@ static PyObject *
 list_append(PyListObject *self, PyObject *object)
 /*[clinic end generated code: output=7c096003a29c0eae input=43a3fe48a7066e91]*/
 {
-    if (app1(self, object) == 0)
-        Py_RETURN_NONE;
-    return NULL;
+    if (_PyList_AppendTakeRef(self, Py_NewRef(object)) < 0) {
+        return NULL;
+    }
+    Py_RETURN_NONE;
 }
 
 /*[clinic input]
@@ -963,9 +965,7 @@ list_extend(PyListObject *self, PyObject *iterable)
             Py_SET_SIZE(self, Py_SIZE(self) + 1);
         }
         else {
-            int status = app1(self, item);
-            Py_DECREF(item);  /* append creates a new ref */
-            if (status < 0)
+            if (_PyList_AppendTakeRef(self, item) < 0)
                 goto error;
         }
     }
index 8f73ea1c01ac5d19852046017987af0ccc1a9ad3..8c1f21b086da932de41c04b23c47eab0ceba8ed9 100644 (file)
@@ -2213,10 +2213,7 @@ handle_eval_breaker:
         TARGET(LIST_APPEND) {
             PyObject *v = POP();
             PyObject *list = PEEK(oparg);
-            int err;
-            err = PyList_Append(list, v);
-            Py_DECREF(v);
-            if (err != 0)
+            if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0)
                 goto error;
             PREDICT(JUMP_BACKWARD_QUICK);
             DISPATCH();
@@ -5044,14 +5041,12 @@ handle_eval_breaker:
             DEOPT_IF(!PyList_Check(list), PRECALL);
             STAT_INC(PRECALL, hit);
             SKIP_CALL();
-            PyObject *arg = TOP();
-            int err = PyList_Append(list, arg);
-            if (err) {
+            PyObject *arg = POP();
+            if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) {
                 goto error;
             }
-            Py_DECREF(arg);
             Py_DECREF(list);
-            STACK_SHRINK(2);
+            STACK_SHRINK(1);
             Py_INCREF(Py_None);
             SET_TOP(Py_None);
             Py_DECREF(callable);