Commit d7ed3bf5 authored by Tim Peters's avatar Tim Peters

Speed tuple comparisons in two ways:

1. Omit the early-out EQ/NE "lengths different?" test.  Was unable to find
   any real code where it triggered, but it always costs.  The same is not
   true of list richcmps, where different-size lists appeared to get
   compared about half the time.
2. Because tuples are immutable, there's no need to refetch the lengths of
   both tuples from memory again on each loop trip.

BUG ALERT:  The tuple (and list) richcmp algorithm is arguably wrong,
because it won't believe there's any difference unless Py_EQ returns false
for some corresponding elements:

>>> class C:
...     def __lt__(x, y): return 1
...     __eq__ = __lt__
...
>>> C() < C()
1
>>> (C(),) < (C(),)
0
>>>

That doesn't make sense -- provided you believe the defn. of C makes sense.
parent fab96cc2
...@@ -376,6 +376,7 @@ tuplerichcompare(PyObject *v, PyObject *w, int op) ...@@ -376,6 +376,7 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
{ {
PyTupleObject *vt, *wt; PyTupleObject *vt, *wt;
int i; int i;
int vlen, wlen;
if (!PyTuple_Check(v) || !PyTuple_Check(w)) { if (!PyTuple_Check(v) || !PyTuple_Check(w)) {
Py_INCREF(Py_NotImplemented); Py_INCREF(Py_NotImplemented);
...@@ -385,19 +386,21 @@ tuplerichcompare(PyObject *v, PyObject *w, int op) ...@@ -385,19 +386,21 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
vt = (PyTupleObject *)v; vt = (PyTupleObject *)v;
wt = (PyTupleObject *)w; wt = (PyTupleObject *)w;
if (vt->ob_size != wt->ob_size && (op == Py_EQ || op == Py_NE)) { vlen = vt->ob_size;
/* Shortcut: if the lengths differ, the tuples differ */ wlen = wt->ob_size;
PyObject *res;
if (op == Py_EQ) /* Note: the corresponding code for lists has an "early out" test
res = Py_False; * here when op is EQ or NE and the lengths differ. That pays there,
else * but Tim was unable to find any real code where EQ/NE tuple
res = Py_True; * compares don't have the same length, so testing for it here would
Py_INCREF(res); * have cost without benefit.
return res; */
}
/* Search for the first index where items are different.
/* Search for the first index where items are different */ * Note that because tuples are immutable, it's safe to reuse
for (i = 0; i < vt->ob_size && i < wt->ob_size; i++) { * vlen and wlen across the comparison calls.
*/
for (i = 0; i < vlen && i < wlen; i++) {
int k = PyObject_RichCompareBool(vt->ob_item[i], int k = PyObject_RichCompareBool(vt->ob_item[i],
wt->ob_item[i], Py_EQ); wt->ob_item[i], Py_EQ);
if (k < 0) if (k < 0)
...@@ -406,19 +409,17 @@ tuplerichcompare(PyObject *v, PyObject *w, int op) ...@@ -406,19 +409,17 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
break; break;
} }
if (i >= vt->ob_size || i >= wt->ob_size) { if (i >= vlen || i >= wlen) {
/* No more items to compare -- compare sizes */ /* No more items to compare -- compare sizes */
int vs = vt->ob_size;
int ws = wt->ob_size;
int cmp; int cmp;
PyObject *res; PyObject *res;
switch (op) { switch (op) {
case Py_LT: cmp = vs < ws; break; case Py_LT: cmp = vlen < wlen; break;
case Py_LE: cmp = ws <= ws; break; case Py_LE: cmp = vlen <= wlen; break;
case Py_EQ: cmp = vs == ws; break; case Py_EQ: cmp = vlen == wlen; break;
case Py_NE: cmp = vs != ws; break; case Py_NE: cmp = vlen != wlen; break;
case Py_GT: cmp = vs > ws; break; case Py_GT: cmp = vlen > wlen; break;
case Py_GE: cmp = vs >= ws; break; case Py_GE: cmp = vlen >= wlen; break;
default: return NULL; /* cannot happen */ default: return NULL; /* cannot happen */
} }
if (cmp) if (cmp)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment