Skip to content

Commit a0ea7a1

Browse files
authored
bpo-47009: Streamline list.append for the common case (GH-31864)
1 parent f877b40 commit a0ea7a1

File tree

4 files changed

+41
-27
lines changed

4 files changed

+41
-27
lines changed

Include/internal/pycore_list.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ struct _Py_list_state {
3737

3838
#define _PyList_ITEMS(op) (_PyList_CAST(op)->ob_item)
3939

40+
extern int
41+
_PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem);
42+
43+
static inline int
44+
_PyList_AppendTakeRef(PyListObject *self, PyObject *newitem)
45+
{
46+
assert(self != NULL && newitem != NULL);
47+
assert(PyList_Check(self));
48+
Py_ssize_t len = PyList_GET_SIZE(self);
49+
Py_ssize_t allocated = self->allocated;
50+
assert((size_t)len + 1 < PY_SSIZE_T_MAX);
51+
if (allocated > len) {
52+
PyList_SET_ITEM(self, len, newitem);
53+
Py_SET_SIZE(self, len + 1);
54+
return 0;
55+
}
56+
return _PyList_AppendTakeRefListResize(self, newitem);
57+
}
4058

4159
#ifdef __cplusplus
4260
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

Objects/listobject.c

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -301,26 +301,27 @@ PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
301301
return ins1((PyListObject *)op, where, newitem);
302302
}
303303

304-
static int
305-
app1(PyListObject *self, PyObject *v)
304+
/* internal, used by _PyList_AppendTakeRef */
305+
int
306+
_PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem)
306307
{
307-
Py_ssize_t n = PyList_GET_SIZE(self);
308-
309-
assert (v != NULL);
310-
assert((size_t)n + 1 < PY_SSIZE_T_MAX);
311-
if (list_resize(self, n+1) < 0)
308+
Py_ssize_t len = PyList_GET_SIZE(self);
309+
assert(self->allocated == -1 || self->allocated == len);
310+
if (list_resize(self, len + 1) < 0) {
311+
Py_DECREF(newitem);
312312
return -1;
313-
314-
Py_INCREF(v);
315-
PyList_SET_ITEM(self, n, v);
313+
}
314+
PyList_SET_ITEM(self, len, newitem);
316315
return 0;
317316
}
318317

319318
int
320319
PyList_Append(PyObject *op, PyObject *newitem)
321320
{
322-
if (PyList_Check(op) && (newitem != NULL))
323-
return app1((PyListObject *)op, newitem);
321+
if (PyList_Check(op) && (newitem != NULL)) {
322+
Py_INCREF(newitem);
323+
return _PyList_AppendTakeRef((PyListObject *)op, newitem);
324+
}
324325
PyErr_BadInternalCall();
325326
return -1;
326327
}
@@ -844,9 +845,10 @@ static PyObject *
844845
list_append(PyListObject *self, PyObject *object)
845846
/*[clinic end generated code: output=7c096003a29c0eae input=43a3fe48a7066e91]*/
846847
{
847-
if (app1(self, object) == 0)
848-
Py_RETURN_NONE;
849-
return NULL;
848+
if (_PyList_AppendTakeRef(self, Py_NewRef(object)) < 0) {
849+
return NULL;
850+
}
851+
Py_RETURN_NONE;
850852
}
851853

852854
/*[clinic input]
@@ -963,9 +965,7 @@ list_extend(PyListObject *self, PyObject *iterable)
963965
Py_SET_SIZE(self, Py_SIZE(self) + 1);
964966
}
965967
else {
966-
int status = app1(self, item);
967-
Py_DECREF(item); /* append creates a new ref */
968-
if (status < 0)
968+
if (_PyList_AppendTakeRef(self, item) < 0)
969969
goto error;
970970
}
971971
}

Python/ceval.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,10 +2213,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
22132213
TARGET(LIST_APPEND) {
22142214
PyObject *v = POP();
22152215
PyObject *list = PEEK(oparg);
2216-
int err;
2217-
err = PyList_Append(list, v);
2218-
Py_DECREF(v);
2219-
if (err != 0)
2216+
if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0)
22202217
goto error;
22212218
PREDICT(JUMP_BACKWARD_QUICK);
22222219
DISPATCH();
@@ -5044,14 +5041,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
50445041
DEOPT_IF(!PyList_Check(list), PRECALL);
50455042
STAT_INC(PRECALL, hit);
50465043
SKIP_CALL();
5047-
PyObject *arg = TOP();
5048-
int err = PyList_Append(list, arg);
5049-
if (err) {
5044+
PyObject *arg = POP();
5045+
if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) {
50505046
goto error;
50515047
}
5052-
Py_DECREF(arg);
50535048
Py_DECREF(list);
5054-
STACK_SHRINK(2);
5049+
STACK_SHRINK(1);
50555050
Py_INCREF(Py_None);
50565051
SET_TOP(Py_None);
50575052
Py_DECREF(callable);

0 commit comments

Comments
 (0)