Micro-optimize list index range checks (GH-9784)

This commit is contained in:
Raymond Hettinger 2018-10-10 20:37:28 -07:00 committed by GitHub
parent 1d26c72e6a
commit f1aa8aed4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 18 additions and 5 deletions

View File

@ -208,6 +208,19 @@ PyList_Size(PyObject *op)
return Py_SIZE(op);
}
static inline int
valid_index(Py_ssize_t i, Py_ssize_t limit)
{
/* The cast to size_t lets us use just a single comparison
to check whether i is in the range: 0 <= i < limit.
See: Section 14.2 "Bounds Checking" in the Agner Fog
optimization manual found at:
https://www.agner.org/optimize/optimizing_cpp.pdf
*/
return (size_t) i < (size_t) limit;
}
static PyObject *indexerr = NULL;
PyObject *
@ -217,7 +230,7 @@ PyList_GetItem(PyObject *op, Py_ssize_t i)
PyErr_BadInternalCall();
return NULL;
}
if (i < 0 || i >= Py_SIZE(op)) {
if (!valid_index(i, Py_SIZE(op))) {
if (indexerr == NULL) {
indexerr = PyUnicode_FromString(
"list index out of range");
@ -240,7 +253,7 @@ PyList_SetItem(PyObject *op, Py_ssize_t i,
PyErr_BadInternalCall();
return -1;
}
if (i < 0 || i >= Py_SIZE(op)) {
if (!valid_index(i, Py_SIZE(op))) {
Py_XDECREF(newitem);
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
@ -426,7 +439,7 @@ list_contains(PyListObject *a, PyObject *el)
static PyObject *
list_item(PyListObject *a, Py_ssize_t i)
{
if (i < 0 || i >= Py_SIZE(a)) {
if (!valid_index(i, Py_SIZE(a))) {
if (indexerr == NULL) {
indexerr = PyUnicode_FromString(
"list index out of range");
@ -749,7 +762,7 @@ list_inplace_repeat(PyListObject *self, Py_ssize_t n)
static int
list_ass_item(PyListObject *a, Py_ssize_t i, PyObject *v)
{
if (i < 0 || i >= Py_SIZE(a)) {
if (!valid_index(i, Py_SIZE(a))) {
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
return -1;
@ -996,7 +1009,7 @@ list_pop_impl(PyListObject *self, Py_ssize_t index)
}
if (index < 0)
index += Py_SIZE(self);
if (index < 0 || index >= Py_SIZE(self)) {
if (!valid_index(index, Py_SIZE(self))) {
PyErr_SetString(PyExc_IndexError, "pop index out of range");
return NULL;
}