Rev 4700: Implement Key_richcompare directly, rather than thunking to tuples. in http://bazaar.launchpad.net/~jameinel/bzr/2.1-memory-consumption
John Arbash Meinel
john at arbash-meinel.com
Fri Sep 11 22:51:19 BST 2009
At http://bazaar.launchpad.net/~jameinel/bzr/2.1-memory-consumption
------------------------------------------------------------
revno: 4700
revision-id: john at arbash-meinel.com-20090911214856-181w4blik8mp1kag
parent: john at arbash-meinel.com-20090910201450-opufy4lpfyqovzkf
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.1-memory-consumption
timestamp: Fri 2009-09-11 16:48:56 -0500
message:
Implement Key_richcompare directly, rather than thunking to tuples.
-------------- next part --------------
=== modified file 'bzrlib/_keys_type_c.c'
--- a/bzrlib/_keys_type_c.c 2009-09-10 20:14:50 +0000
+++ b/bzrlib/_keys_type_c.c 2009-09-11 21:48:56 +0000
@@ -168,7 +168,7 @@
}
static PyObject *
-Key_richcompare(PyObject *v, PyObject *w, int op)
+Key_richcompare_to_tuple(PyObject *v, PyObject *w, int op)
{
PyObject *vt, *wt;
PyObject *vt_to_decref = NULL, *wt_to_decref = NULL;
@@ -210,6 +210,118 @@
return result;
}
+
+static PyObject *
+Key_richcompare(PyObject *v, PyObject *w, int op)
+{
+ Key *vk, *wk;
+ Py_ssize_t vlen, wlen, min_len, i;
+ richcmpfunc string_richcompare;
+
+ if (PyTuple_Check(v) || PyTuple_Check(w)) {
+ /* One of v or w is a tuple, so we go the 'slow' route and cast up to
+ * tuples to compare.
+ */
+ return Key_richcompare_to_tuple(v, w, op);
+ }
+ if (!Key_CheckExact(v) || !Key_CheckExact(w)) {
+ /* Both are not Key objects, and they aren't Tuple objects or the
+ * previous path would have been taken. We don't support comparing with
+ * anything else.
+ */
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ /* Now we know that we have 2 Key objects, so let's compare them.
+ * This code is somewhat borrowed from tuplerichcompare, except we know our
+ * objects are strings, so we get to cheat a bit.
+ */
+ if (v == w) {
+ /* Identical pointers, we can shortcut this easily. */
+ switch (op) {
+ case Py_EQ:case Py_LE:case Py_GE:
+ Py_INCREF(Py_True);
+ return Py_True;
+ case Py_NE:case Py_LT:case Py_GT:
+ Py_INCREF(Py_False);
+ return Py_False;
+ }
+ }
+ vk = (Key*)v;
+ wk = (Key*)w;
+
+ /* It will be rare that we compare tuples of different lengths, so we don't
+ * start by optimizing the length comparision, same as the tuple code
+ */
+ vlen = vk->ob_size;
+ wlen = wk->ob_size;
+ min_len = (vlen < wlen) ? vlen : wlen;
+ string_richcompare = PyString_Type.tp_richcompare;
+ for (i = 0; i < min_len; i++) {
+ PyObject *result;
+ result = string_richcompare((PyObject *)vk->key_bits[i],
+ (PyObject *)wk->key_bits[i],
+ Py_EQ);
+ if (result == NULL) {
+ return NULL; /* Seems to be an error */
+ }
+ if (result == Py_NotImplemented) {
+ PyErr_BadInternalCall();
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (result == Py_False) {
+ /* These strings are not identical
+ * Shortcut for Py_EQ
+ */
+ if (op == Py_EQ) {
+ return result;
+ }
+ Py_DECREF(result);
+ break;
+ }
+ if (result != Py_True) {
+ /* We don't know *what* string_richcompare is returning, but it
+ * isn't correct.
+ */
+ PyErr_BadInternalCall();
+ Py_DECREF(result);
+ return NULL;
+ }
+ Py_DECREF(result);
+ }
+ if (i >= vlen || i >= wlen) {
+ /* No more items to compare -- compare sizes */
+ int cmp;
+ PyObject *res;
+ switch (op) {
+ case Py_LT: cmp = vlen < wlen; break;
+ case Py_LE: cmp = vlen <= wlen; break;
+ case Py_EQ: cmp = vlen == wlen; break;
+ case Py_NE: cmp = vlen != wlen; break;
+ case Py_GT: cmp = vlen > wlen; break;
+ case Py_GE: cmp = vlen >= wlen; break;
+ default: return NULL; /* cannot happen */
+ }
+ if (cmp)
+ res = Py_True;
+ else
+ res = Py_False;
+ Py_INCREF(res);
+ return res;
+ }
+ /* The last item differs, shortcut the Py_NE case */
+ if (op == Py_NE) {
+ Py_INCREF(Py_True);
+ return Py_True;
+ }
+ /* It is some other comparison, go ahead and do the real check. */
+ return string_richcompare((PyObject *)vk->key_bits[i],
+ (PyObject *)wk->key_bits[i],
+ op);
+}
+
+
static Py_ssize_t
Key_length(Key *self)
{
@@ -496,7 +608,7 @@
PyObject *vt, *wt;
PyObject *vt_to_decref = NULL, *wt_to_decref = NULL;
PyObject *result = NULL;
-
+
if (Keys_CheckExact(v)) {
vt = Keys_as_tuple((Keys *)v);
if (vt == NULL) {
@@ -532,7 +644,7 @@
Py_XDECREF(wt_to_decref);
return result;
}
-
+
static PyObject *
Keys_repr(Keys *self)
{
=== modified file 'bzrlib/tests/test__keys_type.py'
--- a/bzrlib/tests/test__keys_type.py 2009-09-10 20:14:50 +0000
+++ b/bzrlib/tests/test__keys_type.py 2009-09-11 21:48:56 +0000
@@ -124,27 +124,66 @@
k = self.module.Key('foo', 'bar', 'baz', 'bing')
self.assertEqual("('foo', 'bar', 'baz', 'bing')", repr(k))
- def test_compare(self):
- k1 = self.module.Key('foo', 'bar')
- k2 = self.module.Key('baz', 'bing')
- k3 = self.module.Key('foo', 'zzz')
- k4 = self.module.Key('foo', 'bar')
- k5 = self.module.Key('foo')
- # Comparison should be done on the keys themselves, and not based on
- # object id, etc.
- self.assertTrue(k1 == k1)
- self.assertTrue(k1 == k4)
- self.assertTrue(k1 != k2)
- self.assertTrue(k1 != k3)
- self.assertTrue(k1 != k5)
- self.assertTrue(k2 < k1)
- self.assertTrue(k2 < k4)
- self.assertTrue(k3 > k1)
- self.assertTrue(k3 > k4)
- self.assertTrue(k5 < k1)
- self.assertTrue(k1 > k5)
- # We should also be able to compare against raw tuples
- self.assertTrue(k1 == ('foo', 'bar'))
+ def assertCompareEqual(self, k1, k2):
+ self.assertTrue(k1 == k2)
+ self.assertTrue(k1 <= k2)
+ self.assertTrue(k1 >= k2)
+ self.assertFalse(k1 != k2)
+ self.assertFalse(k1 < k2)
+ self.assertFalse(k1 > k2)
+
+ def test_compare_same_obj(self):
+ k1 = self.module.Key('foo', 'bar')
+ self.assertCompareEqual(k1, k1)
+
+ def test_compare_equivalent_obj(self):
+ k1 = self.module.Key('foo', 'bar')
+ k2 = self.module.Key('foo', 'bar')
+ self.assertCompareEqual(k1, k2)
+
+ def test_compare_similar_obj(self):
+ k1 = self.module.Key('foo' + ' bar', 'bar' + ' baz')
+ k2 = self.module.Key('fo' + 'o bar', 'ba' + 'r baz')
+ self.assertCompareEqual(k1, k2)
+
+ def assertCompareDifferent(self, k_small, k_big):
+ self.assertFalse(k_small == k_big)
+ self.assertFalse(k_small >= k_big)
+ self.assertFalse(k_small > k_big)
+ self.assertTrue(k_small != k_big)
+ self.assertTrue(k_small <= k_big)
+ self.assertTrue(k_small < k_big)
+
+ def test_compare_all_different_same_width(self):
+ k1 = self.module.Key('baz', 'bing')
+ k2 = self.module.Key('foo', 'bar')
+ self.assertCompareDifferent(k1, k2)
+
+ def test_compare_some_different(self):
+ k1 = self.module.Key('foo', 'bar')
+ k2 = self.module.Key('foo', 'zzz')
+ self.assertCompareDifferent(k1, k2)
+
+ def test_compare_diff_width(self):
+ k1 = self.module.Key('foo')
+ k2 = self.module.Key('foo', 'bar')
+ self.assertCompareDifferent(k1, k2)
+
+ def test_compare_to_tuples(self):
+ k1 = self.module.Key('foo')
+ self.assertCompareEqual(k1, ('foo',))
+ self.assertCompareEqual(('foo',), k1)
+ self.assertCompareDifferent(k1, ('foo', 'bar'))
+ self.assertCompareDifferent(k1, ('foo', 10))
+
+ k2 = self.module.Key('foo', 'bar')
+ self.assertCompareEqual(k2, ('foo', 'bar'))
+ self.assertCompareEqual(('foo', 'bar'), k2)
+ self.assertCompareDifferent(k2, ('foo', 'zzz'))
+ self.assertCompareDifferent(('foo',), k2)
+ self.assertCompareDifferent(('foo', 'aaa'), k2)
+ self.assertCompareDifferent(('baz', 'bing'), k2)
+ self.assertCompareDifferent(('foo', 10), k2)
def test_hash(self):
k = self.module.Key('foo')
More information about the bazaar-commits
mailing list