Skip to content

Commit e6d4440

Browse files
authored
gh-100146: Steal references from stack when building a list (#100147)
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.
1 parent b3722ca commit e6d4440

File tree

5 files changed

+31
-10
lines changed

5 files changed

+31
-10
lines changed

Include/internal/pycore_list.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ typedef struct {
7575
PyListObject *it_seq; /* Set to NULL when iterator is exhausted */
7676
} _PyListIterObject;
7777

78+
extern PyObject *_PyList_FromArraySteal(PyObject *const *src, Py_ssize_t n);
79+
7880
#ifdef __cplusplus
7981
}
8082
#endif
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Improve ``BUILD_LIST`` opcode so that it works similarly to the
2+
``BUILD_TUPLE`` opcode, by stealing references from the stack rather than
3+
repeatedly using stack operations to set list elements. Implementation
4+
details are in a new private API :c:func:`_PyList_FromArraySteal`.

Objects/listobject.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,6 +2565,27 @@ PyList_AsTuple(PyObject *v)
25652565
return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v));
25662566
}
25672567

2568+
PyObject *
2569+
_PyList_FromArraySteal(PyObject *const *src, Py_ssize_t n)
2570+
{
2571+
if (n == 0) {
2572+
return PyList_New(0);
2573+
}
2574+
2575+
PyListObject *list = (PyListObject *)PyList_New(n);
2576+
if (list == NULL) {
2577+
for (Py_ssize_t i = 0; i < n; i++) {
2578+
Py_DECREF(src[i]);
2579+
}
2580+
return NULL;
2581+
}
2582+
2583+
PyObject **dst = list->ob_item;
2584+
memcpy(dst, src, n * sizeof(PyObject *));
2585+
2586+
return (PyObject *)list;
2587+
}
2588+
25682589
/*[clinic input]
25692590
list.index
25702591

Python/bytecodes.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,13 +1390,10 @@ dummy_func(
13901390

13911391
// stack effect: (__array[oparg] -- __0)
13921392
inst(BUILD_LIST) {
1393-
PyObject *list = PyList_New(oparg);
1393+
STACK_SHRINK(oparg);
1394+
PyObject *list = _PyList_FromArraySteal(stack_pointer, oparg);
13941395
if (list == NULL)
13951396
goto error;
1396-
while (--oparg >= 0) {
1397-
PyObject *item = POP();
1398-
PyList_SET_ITEM(list, oparg, item);
1399-
}
14001397
PUSH(list);
14011398
}
14021399

Python/generated_cases.c.h

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)