From 9379cd4d1f9a789f0e45065b199946ab0ea2e3fc Mon Sep 17 00:00:00 2001 From: "L. Pereira" Date: Fri, 9 Dec 2022 13:19:04 -0800 Subject: [PATCH] gh-100146: Steal references from stack when building a list When executing the BUILD_LIST opcode, steal the references from the stack, in a manner similar to the BUILD_TUPLE opcode. Implement this by offloading the logic to a new private API, _PyList_FromArraySteal(), that works similarly to _PyTuple_FromArraySteal(). This way, instead of performing multiple stack pointer adjustments while the list is being initialized, the stack is adjusted only once and a fast memory copy operation is performed in one fell swoop. --- Include/internal/pycore_list.h | 2 ++ ...-12-09-13-18-42.gh-issue-100146.xLVKg0.rst | 4 ++++ Objects/listobject.c | 21 +++++++++++++++++++ Python/bytecodes.c | 7 ++----- Python/generated_cases.c.h | 7 ++----- 5 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index 628267cc8a9618..2fcbe12cd6559e 100644 --- a/Include/internal/pycore_list.h +++ b/Include/internal/pycore_list.h @@ -75,6 +75,8 @@ typedef struct { PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ } _PyListIterObject; +extern PyObject *_PyList_FromArraySteal(PyObject *const *src, Py_ssize_t n); + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst b/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst new file mode 100644 index 00000000000000..8023a366a0e618 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-12-09-13-18-42.gh-issue-100146.xLVKg0.rst @@ -0,0 +1,4 @@ +Improve ``BUILD_LIST`` opcode so that it works similarly to the +``BUILD_TUPLE`` opcode, by stealing references from the stack rather than +repeatedly using stack operations to set list elements. Implementation +details are in a new private API :c:func:`_PyList_FromArraySteal`. diff --git a/Objects/listobject.c b/Objects/listobject.c index 1d32915b17a14b..7de3ec12d00e68 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2557,6 +2557,27 @@ PyList_AsTuple(PyObject *v) return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v)); } +PyObject * +_PyList_FromArraySteal(PyObject *const *src, Py_ssize_t n) +{ + if (n == 0) { + return PyList_New(0); + } + + PyListObject *list = (PyListObject *)PyList_New(n); + if (list == NULL) { + for (Py_ssize_t i = 0; i < n; i++) { + Py_DECREF(src[i]); + } + return NULL; + } + + PyObject **dst = list->ob_item; + memcpy(dst, src, n * sizeof(PyObject *)); + + return (PyObject *)list; +} + /*[clinic input] list.index diff --git a/Python/bytecodes.c b/Python/bytecodes.c index c56f1d3ef9f498..ff7a982c1cb112 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1441,13 +1441,10 @@ dummy_func( // stack effect: (__array[oparg] -- __0) inst(BUILD_LIST) { - PyObject *list = PyList_New(oparg); + STACK_SHRINK(oparg); + PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg); if (list == NULL) goto error; - while (--oparg >= 0) { - PyObject *item = POP(); - PyList_SET_ITEM(list, oparg, item); - } PUSH(list); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 45382a466b1ca9..08fcb57d7db5a1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1603,13 +1603,10 @@ } TARGET(BUILD_LIST) { - PyObject *list = PyList_New(oparg); + STACK_SHRINK(oparg); + PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg); if (list == NULL) goto error; - while (--oparg >= 0) { - PyObject *item = POP(); - PyList_SET_ITEM(list, oparg, item); - } PUSH(list); DISPATCH(); }