Antoine Pitrou <[EMAIL PROTECTED]> added the comment:
Hello,
> You may get better timings if you more the types-are-equal test inside
> the types-i-know test.
I get no discernable difference.
> In general, I'm not too keen on adding this kind of dispatch code to
> ceval.c. It saves the time spent in PyObject_RichCompare() trying to
> figure out where to delegate the work.
Some minimal type testing is necessary if we want to implement the
identity-implies-equality optimization for some types without breaking
the fact that e.g. NaN != NaN. This optimization is important when
dealing with e.g. interned strings. There could be a special flag in the
type structure signaling that the optimization is safe.
I'm attaching a patch which reduces the additional dispatch to a
minimum. The speedup on pybench is smaller, but there is no significant
slowdown for "other" comparisons.
Test minimum run-time average run-time
this other diff this other diff
-------------------------------------------------------------------------------
CompareFloats: 176ms 173ms +1.9% 180ms 175ms +3.2%
CompareFloatsIntegers: 237ms 234ms +1.0% 251ms 240ms +4.6%
CompareIntegers: 266ms 276ms -3.6% 266ms 278ms -4.3%
CompareInternedStrings: 160ms 261ms -38.6% 161ms 261ms -38.4%
CompareLongs: 156ms 166ms -6.1% 156ms 167ms -7.1%
CompareStrings: 167ms 172ms -2.9% 170ms 173ms -1.9%
-------------------------------------------------------------------------------
Totals: 1161ms 1281ms -9.4% 1184ms 1295ms -8.6%
Added file: http://bugs.python.org/file12021/cmps4.patch
_______________________________________
Python tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue3106>
_______________________________________
diff -r c8ad1bd796f0 Objects/longobject.c
--- a/Objects/longobject.c Sun Nov 16 19:33:53 2008 +0100
+++ b/Objects/longobject.c Sun Nov 16 21:28:08 2008 +0100
@@ -2209,37 +2209,60 @@
static int
long_compare(PyLongObject *a, PyLongObject *b)
{
- Py_ssize_t sign;
-
- if (Py_SIZE(a) != Py_SIZE(b)) {
- if (ABS(Py_SIZE(a)) == 0 && ABS(Py_SIZE(b)) == 0)
- sign = 0;
- else
- sign = Py_SIZE(a) - Py_SIZE(b);
+ Py_ssize_t sa = Py_SIZE(a), sb = Py_SIZE(b), i;
+ if (sa != sb) {
+ return sa > sb ? 1 : -1;
}
- else {
- Py_ssize_t i = ABS(Py_SIZE(a));
- while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i])
- ;
- if (i < 0)
- sign = 0;
- else {
- sign = (int)a->ob_digit[i] - (int)b->ob_digit[i];
- if (Py_SIZE(a) < 0)
- sign = -sign;
+ i = ABS(sa);
+ while (--i >= 0) {
+ digit ad = a->ob_digit[i], bd = b->ob_digit[i];
+ if (ad != bd) {
+ if (sa < 0)
+ return ad < bd ? 1 : -1;
+ else
+ return ad > bd ? 1 : -1;
}
}
- return sign < 0 ? -1 : sign > 0 ? 1 : 0;
+ return 0;
}
+
+#define TEST_COND(cond) \
+ ((cond) ? Py_True : Py_False)
static PyObject *
long_richcompare(PyObject *self, PyObject *other, int op)
{
- PyObject *result;
+ int cmp;
+ PyObject *v;
CHECK_BINOP(self, other);
- result = Py_CmpToRich(op, long_compare((PyLongObject*)self,
- (PyLongObject*)other));
- return result;
+ cmp = long_compare((PyLongObject*)self,
+ (PyLongObject*)other);
+ /* Convert the return value to a Boolean */
+ switch (op) {
+ case Py_EQ:
+ v = TEST_COND(cmp == 0);
+ break;
+ case Py_NE:
+ v = TEST_COND(cmp != 0);
+ break;
+ case Py_LE:
+ v = TEST_COND(cmp <= 0);
+ break;
+ case Py_GE:
+ v = TEST_COND(cmp >= 0);
+ break;
+ case Py_LT:
+ v = TEST_COND(cmp == -1);
+ break;
+ case Py_GT:
+ v = TEST_COND(cmp == 1);
+ break;
+ default:
+ PyErr_BadArgument();
+ return NULL;
+ }
+ Py_INCREF(v);
+ return v;
}
static long
diff -r c8ad1bd796f0 Objects/unicodeobject.c
--- a/Objects/unicodeobject.c Sun Nov 16 19:33:53 2008 +0100
+++ b/Objects/unicodeobject.c Sun Nov 16 21:28:08 2008 +0100
@@ -6508,81 +6508,62 @@
return 0;
}
+
+#define TEST_COND(cond) \
+ ((cond) ? Py_True : Py_False)
+
PyObject *PyUnicode_RichCompare(PyObject *left,
PyObject *right,
int op)
{
int result;
- result = PyUnicode_Compare(left, right);
- if (result == -1 && PyErr_Occurred())
- goto onError;
-
- /* Convert the return value to a Boolean */
- switch (op) {
- case Py_EQ:
- result = (result == 0);
- break;
- case Py_NE:
- result = (result != 0);
- break;
- case Py_LE:
- result = (result <= 0);
- break;
- case Py_GE:
- result = (result >= 0);
- break;
- case Py_LT:
- result = (result == -1);
- break;
- case Py_GT:
- result = (result == 1);
- break;
- }
- return PyBool_FromLong(result);
-
- onError:
-
- /* Standard case
-
- Type errors mean that PyUnicode_FromObject() could not convert
- one of the arguments (usually the right hand side) to Unicode,
- ie. we can't handle the comparison request. However, it is
- possible that the other object knows a comparison method, which
- is why we return Py_NotImplemented to give the other object a
- chance.
-
- */
- if (PyErr_ExceptionMatches(PyExc_TypeError)) {
- PyErr_Clear();
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
- }
- if (op != Py_EQ && op != Py_NE)
- return NULL;
-
- /* Equality comparison.
-
- This is a special case: we silence any PyExc_UnicodeDecodeError
- and instead turn it into a PyErr_UnicodeWarning.
-
- */
- if (!PyErr_ExceptionMatches(PyExc_UnicodeDecodeError))
- return NULL;
- PyErr_Clear();
- if (PyErr_WarnEx(PyExc_UnicodeWarning,
- (op == Py_EQ) ?
- "equal comparison "
- "failed to convert both arguments to str - "
- "interpreting them as being unequal"
- :
- "Unicode unequal comparison "
- "failed to convert both arguments to str - "
- "interpreting them as being unequal",
- 1) < 0)
- return NULL;
- result = (op == Py_NE);
- return PyBool_FromLong(result);
+ if (PyUnicode_Check(left) && PyUnicode_Check(right)) {
+ PyObject *v;
+ if (((PyUnicodeObject *) left)->length !=
+ ((PyUnicodeObject *) right)->length) {
+ if (op == Py_EQ) {
+ Py_INCREF(Py_False);
+ return Py_False;
+ }
+ if (op == Py_NE) {
+ Py_INCREF(Py_True);
+ return Py_True;
+ }
+ }
+ result = unicode_compare((PyUnicodeObject *)left,
+ (PyUnicodeObject *)right);
+
+ /* Convert the return value to a Boolean */
+ switch (op) {
+ case Py_EQ:
+ v = TEST_COND(result == 0);
+ break;
+ case Py_NE:
+ v = TEST_COND(result != 0);
+ break;
+ case Py_LE:
+ v = TEST_COND(result <= 0);
+ break;
+ case Py_GE:
+ v = TEST_COND(result >= 0);
+ break;
+ case Py_LT:
+ v = TEST_COND(result == -1);
+ break;
+ case Py_GT:
+ v = TEST_COND(result == 1);
+ break;
+ default:
+ PyErr_BadArgument();
+ return NULL;
+ }
+ Py_INCREF(v);
+ return v;
+ }
+
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
}
int PyUnicode_Contains(PyObject *container,
diff -r c8ad1bd796f0 Python/ceval.c
--- a/Python/ceval.c Sun Nov 16 19:33:53 2008 +0100
+++ b/Python/ceval.c Sun Nov 16 21:28:08 2008 +0100
@@ -3782,6 +3782,28 @@
}
res = PyErr_GivenExceptionMatches(v, w);
break;
+ case PyCmp_EQ:
+ case PyCmp_LE:
+ case PyCmp_GE:
+ res = 1;
+ case PyCmp_NE:
+ case PyCmp_LT:
+ case PyCmp_GT:
+ if (v == w) {
+ /* Optimization note: we must be careful, a few objects
can be
+ unequal to themselves (e.g. float("nan")). To keep
things
+ simple and fast, we only check for common builtin
types.
+ The check could be replaced by a dedicated flag in
the
+ type structure. */
+ if (PyType_FastSubclass(Py_TYPE(v),
Py_TPFLAGS_LONG_SUBCLASS
+ | Py_TPFLAGS_LIST_SUBCLASS
+ | Py_TPFLAGS_BYTES_SUBCLASS
+ | Py_TPFLAGS_TUPLE_SUBCLASS
+ | Py_TPFLAGS_INT_SUBCLASS
+ | Py_TPFLAGS_UNICODE_SUBCLASS
+ | Py_TPFLAGS_DICT_SUBCLASS))
+ break;
+ }
default:
return PyObject_RichCompare(v, w, op);
}
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com