Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d7ed3bf

Browse files
committed
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.
1 parent fab96cc commit d7ed3bf

1 file changed

Lines changed: 23 additions & 22 deletions

File tree

Objects/tupleobject.c

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
376376
{
377377
PyTupleObject *vt, *wt;
378378
int i;
379+
int vlen, wlen;
379380

380381
if (!PyTuple_Check(v) || !PyTuple_Check(w)) {
381382
Py_INCREF(Py_NotImplemented);
@@ -385,19 +386,21 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
385386
vt = (PyTupleObject *)v;
386387
wt = (PyTupleObject *)w;
387388

388-
if (vt->ob_size != wt->ob_size && (op == Py_EQ || op == Py_NE)) {
389-
/* Shortcut: if the lengths differ, the tuples differ */
390-
PyObject *res;
391-
if (op == Py_EQ)
392-
res = Py_False;
393-
else
394-
res = Py_True;
395-
Py_INCREF(res);
396-
return res;
397-
}
398-
399-
/* Search for the first index where items are different */
400-
for (i = 0; i < vt->ob_size && i < wt->ob_size; i++) {
389+
vlen = vt->ob_size;
390+
wlen = wt->ob_size;
391+
392+
/* Note: the corresponding code for lists has an "early out" test
393+
* here when op is EQ or NE and the lengths differ. That pays there,
394+
* but Tim was unable to find any real code where EQ/NE tuple
395+
* compares don't have the same length, so testing for it here would
396+
* have cost without benefit.
397+
*/
398+
399+
/* Search for the first index where items are different.
400+
* Note that because tuples are immutable, it's safe to reuse
401+
* vlen and wlen across the comparison calls.
402+
*/
403+
for (i = 0; i < vlen && i < wlen; i++) {
401404
int k = PyObject_RichCompareBool(vt->ob_item[i],
402405
wt->ob_item[i], Py_EQ);
403406
if (k < 0)
@@ -406,19 +409,17 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
406409
break;
407410
}
408411

409-
if (i >= vt->ob_size || i >= wt->ob_size) {
412+
if (i >= vlen || i >= wlen) {
410413
/* No more items to compare -- compare sizes */
411-
int vs = vt->ob_size;
412-
int ws = wt->ob_size;
413414
int cmp;
414415
PyObject *res;
415416
switch (op) {
416-
case Py_LT: cmp = vs < ws; break;
417-
case Py_LE: cmp = ws <= ws; break;
418-
case Py_EQ: cmp = vs == ws; break;
419-
case Py_NE: cmp = vs != ws; break;
420-
case Py_GT: cmp = vs > ws; break;
421-
case Py_GE: cmp = vs >= ws; break;
417+
case Py_LT: cmp = vlen < wlen; break;
418+
case Py_LE: cmp = vlen <= wlen; break;
419+
case Py_EQ: cmp = vlen == wlen; break;
420+
case Py_NE: cmp = vlen != wlen; break;
421+
case Py_GT: cmp = vlen > wlen; break;
422+
case Py_GE: cmp = vlen >= wlen; break;
422423
default: return NULL; /* cannot happen */
423424
}
424425
if (cmp)

0 commit comments

Comments
 (0)