Rev 4723: Special case the empty tuple as a singleton. in http://bazaar.launchpad.net/~jameinel/bzr/2.1-static-tuple

John Arbash Meinel john at arbash-meinel.com
Wed Sep 30 20:15:41 BST 2009


At http://bazaar.launchpad.net/~jameinel/bzr/2.1-static-tuple

------------------------------------------------------------
revno: 4723
revision-id: john at arbash-meinel.com-20090930191534-2mi59m6qxtsoaw7j
parent: john at arbash-meinel.com-20090930185201-gjsozmi8khwf21uh
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.1-static-tuple
timestamp: Wed 2009-09-30 14:15:34 -0500
message:
  Special case the empty tuple as a singleton.
  tuple() already had this property, we just added it to StaticTuple.
  After doing that and fixing up one more code path in BtreeSerializer (when there
  are no ref lists), memory is down to:
    98876KB w/ 6.0MB in the intern dict.
  So it seems we had 4MB of tuples in the 'no-ref-list' cases, and just
  switching it over to StaticTuple caused no memory decrease, which means there
  were a lot of empty tuples.
  Makes sense, since chk_bytes is the bulk of our objects and has no ref lists
  
  Note that Launchpad's memory consumption is also down to:
    268580KB w/ 24MB intern dict.
  down from (bzr.dev):
    326656KB
  A 1.22:1 reduction.
-------------- next part --------------
=== modified file 'bzrlib/_btree_serializer_pyx.pyx'
--- a/bzrlib/_btree_serializer_pyx.pyx	2009-09-30 18:52:01 +0000
+++ b/bzrlib/_btree_serializer_pyx.pyx	2009-09-30 19:15:34 +0000
@@ -275,8 +275,8 @@
             if last != self._start:
                 # unexpected reference data present
                 return -1
-            node_value = (value, ())
-        PyList_Append(self.keys, (key, node_value))
+            node_value = StaticTuple(value, StaticTuple())
+        PyList_Append(self.keys, StaticTuple(key, node_value))
         return 0
 
     def parse(self):

=== modified file 'bzrlib/_static_tuple_c.c'
--- a/bzrlib/_static_tuple_c.c	2009-09-30 18:37:54 +0000
+++ b/bzrlib/_static_tuple_c.c	2009-09-30 19:15:34 +0000
@@ -35,6 +35,8 @@
 #endif
 
 
+/* The one and only StaticTuple with no values */
+static StaticTuple *_empty_tuple = NULL;
 static PyObject *_interned_keys = NULL;
 
 
@@ -141,6 +143,11 @@
         return NULL;
     }
 
+    if (size == 0) {
+        assert(_empty_tuple != NULL);
+        Py_INCREF(_empty_tuple);
+        return (PyObject *)_empty_tuple;
+    }
     /* Note that we use PyObject_NewVar because we want to allocate a variable
      * width entry. However we *aren't* truly a PyVarObject because we don't
      * use a long for ob_size. Instead we use a plain 'size' that is an int,
@@ -186,17 +193,10 @@
             " takes from 0 to 255 key bits");
         return NULL;
     }
-    self = (StaticTuple *)(type->tp_alloc(type, len));
+    self = (StaticTuple *)StaticTuple_New(len);
     if (self == NULL) {
         return NULL;
     }
-    self->size = len;
-    self->flags = 0;
-    self->_unused0 = 0;
-    self->_unused1 = 0;
-#if STATIC_TUPLE_HAS_HASH
-    self->hash = -1;
-#endif
     for (i = 0; i < len; ++i) {
         obj = PyTuple_GET_ITEM(args, i);
         if (!PyString_CheckExact(obj) && !StaticTuple_CheckExact(obj)) {
@@ -654,6 +654,7 @@
 init_static_tuple_c(void)
 {
     PyObject* m;
+    StaticTuple *key;
 
     if (PyType_Ready(&StaticTuple_Type) < 0)
         return;
@@ -675,4 +676,22 @@
         Py_INCREF(_interned_keys);
         PyModule_AddObject(m, "_interned_keys", _interned_keys);
     }
+
+    // We need to create the empty tuple
+    key = PyObject_NewVar(StaticTuple, &StaticTuple_Type, 0);
+    if (key == NULL) {
+        return;
+    }
+    key->size = 0;
+    key->flags = 0;
+    key->_unused0 = 0;
+    key->_unused1 = 0;
+#if STATIC_TUPLE_HAS_HASH
+    key->hash = -1;
+#endif
+    _empty_tuple = StaticTuple_intern(key);
+    assert(_empty_tuple == key);
+    Py_INCREF(_empty_tuple); // Never dies
+    Py_INCREF(_empty_tuple); // for the module
+    PyModule_AddObject(m, "_empty_tuple", (PyObject *)_empty_tuple);
 }

=== modified file 'bzrlib/_static_tuple_py.py'
--- a/bzrlib/_static_tuple_py.py	2009-09-30 18:37:54 +0000
+++ b/bzrlib/_static_tuple_py.py	2009-09-30 19:15:34 +0000
@@ -26,6 +26,11 @@
 
     __slots__ = ('_tuple',)
 
+    def __new__(cls, *args):
+        if not args and _empty_tuple is not None:
+            return _empty_tuple
+        return super(StaticTuple, cls).__new__(cls, *args)
+
     def __init__(self, *args):
         """Create a new 'StaticTuple'"""
         for bit in args:
@@ -65,4 +70,6 @@
         return _interned_keys.setdefault(self, self)
 
 
+_empty_tuple = None
+_empty_tuple = StaticTuple()
 _interned_keys = {}

=== modified file 'bzrlib/tests/test__static_tuple.py'
--- a/bzrlib/tests/test__static_tuple.py	2009-09-30 18:37:54 +0000
+++ b/bzrlib/tests/test__static_tuple.py	2009-09-30 19:15:34 +0000
@@ -272,6 +272,10 @@
         else:
             self.assertEqual(sorted(strs), sorted(scanner.get_referents(k)))
 
+    def test_empty_is_singleton(self):
+        key = self.module.StaticTuple()
+        self.assertIs(key, self.module._empty_tuple)
+
     def test_intern(self):
         unique_str1 = 'unique str ' + osutils.rand_chars(20)
         unique_str2 = 'unique str ' + osutils.rand_chars(20)



More information about the bazaar-commits mailing list