From a0ea7a116ce52a178c02d42b684089758bd7f355 Mon Sep 17 00:00:00 2001 From: Dennis Sweeney <36520290+sweeneyde@users.noreply.github.com> Date: Fri, 1 Apr 2022 06:23:42 -0400 Subject: bpo-47009: Streamline list.append for the common case (GH-31864) --- Include/internal/pycore_list.h | 18 +++++++++++ .../2022-03-14-09-45-10.bpo-47009.ZI05b5.rst | 1 + Objects/listobject.c | 36 +++++++++++----------- Python/ceval.c | 13 +++----- 4 files changed, 41 insertions(+), 27 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-14-09-45-10.bpo-47009.ZI05b5.rst diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index 0717a1f..860dce1 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 0000000..0c65c34 --- /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 d50633d..ccb9b91 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 8f73ea1..8c1f21b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -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); -- cgit v0.12