-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
bpo-47009: Streamline list.append for the common case #31864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the extra function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried something like that and the results are on the bpo issue, but it seems there is a significant benefit to having the separate as-small-as-possible function that always gets inlined as opposed to a slightly longer version that we let the compiler figure out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Presumably the compiler isn't inlining the function because it isn't hot enough. Do you have benchmark numbers for the standard suite? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://gist.github.com/sweeneyde/6cbbe1c9d216d117370a809c704b6cfc Geometric mean: 1.00x faster There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, IMO there is a relative lack of list comprehensions in the pyperformance suite. |
||
{ | ||
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; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for an inline function.
Let the LTO pass decide if it wants to inline it.