[Python-checkins] bpo-47009: Streamline list.append for the common case (GH-31864)
markshannon
webhook-mailer at python.org
Fri Apr 1 06:24:14 EDT 2022
https://github.com/python/cpython/commit/a0ea7a116ce52a178c02d42b684089758bd7f355
commit: a0ea7a116ce52a178c02d42b684089758bd7f355
branch: main
author: Dennis Sweeney <36520290+sweeneyde at users.noreply.github.com>
committer: markshannon <mark at hotpy.org>
date: 2022-04-01T11:23:42+01:00
summary:
bpo-47009: Streamline list.append for the common case (GH-31864)
files:
A Misc/NEWS.d/next/Core and Builtins/2022-03-14-09-45-10.bpo-47009.ZI05b5.rst
M Include/internal/pycore_list.h
M Objects/listobject.c
M Python/ceval.c
diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h
index 0717a1f9563a2..860dce1fd5d39 100644
--- a/Include/internal/pycore_list.h
+++ b/Include/internal/pycore_list.h
@@ -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
index 0000000000000..0c65c34d310ec
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-14-09-45-10.bpo-47009.ZI05b5.rst
@@ -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.
diff --git a/Objects/listobject.c b/Objects/listobject.c
index d50633d2b3132..ccb9b91ba930d 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -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;
}
}
diff --git a/Python/ceval.c b/Python/ceval.c
index 8f73ea1c01ac5..8c1f21b086da9 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -2213,10 +2213,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
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 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
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);
More information about the Python-checkins
mailing list