Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Lib/test/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,13 @@ def __eq__(self, other):
lst = [X(), X()]
X() in lst

def test_eq_identity_shortcut(self):
# PyObject_RichCompareBool() treats identity as equality, even for
# objects that are not equal to themselves (see seq_tests).
nan = float("nan")
self.assertIn(nan, [nan])
self.assertNotIn(float("nan"), [nan])

def test_tier2_invalidates_iterator(self):
# GH-121012
for _ in range(100):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Speed up ``x in list`` for lists of integers, floats and strings by specializing the per-item equality check.
68 changes: 66 additions & 2 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "pycore_stackref.h" // _Py_TryIncrefCompareStackRef()
#include "pycore_tuple.h" // _PyTuple_FromArraySteal()
#include "pycore_typeobject.h" // _Py_TYPE_VERSION_LIST
#include "pycore_unicodeobject.h" // _PyUnicode_Equal()
#include <stddef.h>

/*[clinic input]
Expand Down Expand Up @@ -707,17 +708,80 @@ list_length(PyObject *a)
return PyList_GET_SIZE(a);
}

static inline PyTypeObject *
list_fast_eq_type(PyObject *value)
{
if (value != NULL) {
PyTypeObject *type = Py_TYPE(value);
if (type == &PyLong_Type || type == &PyFloat_Type ||
type == &PyUnicode_Type) {
return type;
}
}
return NULL;
}

static inline int
list_item_eq(PyObject *item, PyObject *value, PyTypeObject *fast_type)
{
if (fast_type != NULL && Py_TYPE(item) == fast_type) {
int res;
if (item == value) {
return 1;
}
if (fast_type == &PyLong_Type) {
PyLongObject *v = (PyLongObject *)item;
PyLongObject *w = (PyLongObject *)value;
if (!_PyLong_BothAreCompact(v, w)) {
return PyObject_RichCompareBool(item, value, Py_EQ);
}
res = _PyLong_CompactValue(v) == _PyLong_CompactValue(w);
}
else if (fast_type == &PyFloat_Type) {
res = PyFloat_AS_DOUBLE(item) == PyFloat_AS_DOUBLE(value);
}
else {
assert(fast_type == &PyUnicode_Type);
res = _PyUnicode_Equal(item, value);
}
assert(res == PyObject_RichCompareBool(item, value, Py_EQ));
return res;
}
return PyObject_RichCompareBool(item, value, Py_EQ);
}

static Py_NO_INLINE int
list_contains_generic(PyListObject *a, PyObject *el)
{
for (Py_ssize_t i = 0; ; i++) {
PyObject *item = list_get_item_ref(a, i);
if (item == NULL) {
// out-of-bounds
return 0;
}
int cmp = PyObject_RichCompareBool(item, el, Py_EQ);
Py_DECREF(item);
if (cmp != 0) {
return cmp;
}
}
return 0;
}

static int
list_contains(PyObject *aa, PyObject *el)
{

PyTypeObject *fast_type = list_fast_eq_type(el);
if (fast_type == NULL) {
return list_contains_generic((PyListObject *)aa, el);
}
for (Py_ssize_t i = 0; ; i++) {
PyObject *item = list_get_item_ref((PyListObject *)aa, i);
if (item == NULL) {
// out-of-bounds
return 0;
}
int cmp = PyObject_RichCompareBool(item, el, Py_EQ);
int cmp = list_item_eq(item, el, fast_type);
Py_DECREF(item);
if (cmp != 0) {
return cmp;
Expand Down
Loading