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