]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- scale back _Dispatch and _JoinedDispatcher to use a __getitem__ scheme
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Jan 2015 00:05:31 +0000 (19:05 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Jan 2015 00:05:31 +0000 (19:05 -0500)
to start up listener collections; this pulls the overhead off of construction
and makes performance much like the descriptor version, while still allowing
slots.   Fix up some profiles.

lib/sqlalchemy/event/base.py
test/base/test_events.py
test/profiles.txt

index 962d850c2ad81b9e8d7bc8412dfdb3ecfdb810c8..2d546888633b60446c51e1f7f58fb3be8ab3b8e3 100644 (file)
@@ -67,23 +67,35 @@ class _Dispatch(object):
 
     # in one ORM edge case, an attribute is added to _Dispatch,
     # so __dict__ is used in just that case and potentially others.
-    __slots__ = '_parent', '_instance_cls', '__dict__'
+    __slots__ = '_parent', '_instance_cls', '__dict__', '_empty_listeners'
 
-    _empty_listeners = weakref.WeakKeyDictionary()
+    _empty_listener_reg = weakref.WeakKeyDictionary()
 
     def __init__(self, parent, instance_cls=None):
         self._parent = parent
         self._instance_cls = instance_cls
         if instance_cls:
             try:
-                _empty_listeners = self._empty_listeners[instance_cls]
+                self._empty_listeners = self._empty_listener_reg[instance_cls]
             except KeyError:
-                _empty_listeners = self._empty_listeners[instance_cls] = [
-                    _EmptyListener(ls, instance_cls)
-                    for ls in parent._event_descriptors
-                ]
-            for ls in _empty_listeners:
-                setattr(self, ls.name, ls)
+                self._empty_listeners = \
+                    self._empty_listener_reg[instance_cls] = dict(
+                        (ls.name, _EmptyListener(ls, instance_cls))
+                        for ls in parent._event_descriptors
+                    )
+        else:
+            self._empty_listeners = {}
+
+    def __getattr__(self, name):
+        # assign EmptyListeners as attributes on demand
+        # to reduce startup time for new dispatch objects
+        try:
+            ls = self._empty_listeners[name]
+        except KeyError:
+            raise AttributeError(name)
+        else:
+            setattr(self, ls.name, ls)
+            return ls
 
     @property
     def _event_descriptors(self):
@@ -244,9 +256,14 @@ class _JoinedDispatcher(object):
         self.local = local
         self.parent = parent
         self._instance_cls = self.local._instance_cls
-        for ls in local._event_descriptors:
-            setattr(self, ls.name, _JoinedListener(
-                parent, ls.name, ls))
+
+    def __getattr__(self, name):
+        # assign _JoinedListeners as attributes on demand
+        # to reduce startup time for new dispatch objects
+        ls = getattr(self.local, name)
+        jl = _JoinedListener(self.parent, ls.name, ls)
+        setattr(self, ls.name, jl)
+        return jl
 
     @property
     def _listen(self):
index 1449bfab0fd278cecd895381cf1b98395e5ad6ae..8cfbd0180a1cc7f343a4d72a7d5c6cfca6bc4774 100644 (file)
@@ -154,16 +154,22 @@ class EventsTest(fixtures.TestBase):
         t2 = self.Target()
         t1.dispatch.event_one(5, 6)
         t2.dispatch.event_one(5, 6)
-        assert t1.dispatch.event_one in \
-            self.Target.dispatch._empty_listeners[self.Target]
+        is_(
+            self.Target.dispatch._empty_listener_reg[self.Target]['event_one'],
+            t1.dispatch.event_one
+        )
 
         @event.listens_for(t1, "event_one")
         def listen_two(x, y):
             pass
-        assert t1.dispatch.event_one not in \
-            self.Target.dispatch._empty_listeners[self.Target]
-        assert t2.dispatch.event_one in \
-            self.Target.dispatch._empty_listeners[self.Target]
+        is_not_(
+            self.Target.dispatch._empty_listener_reg[self.Target]['event_one'],
+            t1.dispatch.event_one
+        )
+        is_(
+            self.Target.dispatch._empty_listener_reg[self.Target]['event_one'],
+            t2.dispatch.event_one
+        )
 
     def test_immutable_methods(self):
         t1 = self.Target()
index c11000e29284d2fcc6f61e6591fea4d268e82894..507762e652f5cf15cdb581449fc81fcbab7e9eb9 100644 (file)
@@ -133,8 +133,8 @@ test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_postgresql_psycop
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_cextensions 19280
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_nocextensions 28297
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_postgresql_psycopg2_nocextensions 29138
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_sqlite_pysqlite_cextensions 32398
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_sqlite_pysqlite_nocextensions 37327
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_sqlite_pysqlite_cextensions 20352
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_sqlite_pysqlite_nocextensions 29355
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.4_postgresql_psycopg2_cextensions 20135
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.4_postgresql_psycopg2_nocextensions 29138
 
@@ -147,7 +147,7 @@ test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_postgresql
 test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_cextensions 27144
 test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_nocextensions 30149
 test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_postgresql_psycopg2_nocextensions 29068
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_sqlite_pysqlite_cextensions 32197
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_sqlite_pysqlite_cextensions 26208
 test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_sqlite_pysqlite_nocextensions 31179
 test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.4_postgresql_psycopg2_cextensions 26065
 test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.4_postgresql_psycopg2_nocextensions 29068
@@ -214,11 +214,11 @@ test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_cexte
 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_nocextensions 117,18
 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_cextensions 117,18
 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_nocextensions 117,18
-test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_cextensions 117,18
-test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_nocextensions 117,18
+test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_cextensions 91,18
+test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_nocextensions 91,18
 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_postgresql_psycopg2_nocextensions 122,19
-test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_sqlite_pysqlite_cextensions 122,19
-test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_sqlite_pysqlite_nocextensions 122,19
+test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_sqlite_pysqlite_cextensions 94,19
+test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_sqlite_pysqlite_nocextensions 94,19
 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.4_postgresql_psycopg2_cextensions 122,19
 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.4_postgresql_psycopg2_nocextensions 122,19
 
@@ -351,8 +351,8 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.4_sqlite_pysqlite
 
 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation
 
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_cextensions 5562,292,3697,11893,1106,1968,2433
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_nocextensions 5606,292,3929,13595,1223,2011,2692
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_cextensions 5892,292,3697,11893,1106,1968,2433
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_nocextensions 5936,292,3929,13595,1223,2011,2692
 test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.3_postgresql_psycopg2_cextensions 5497,274,3609,11647,1097,1921,2486
 test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.3_postgresql_psycopg2_nocextensions 5519,274,3705,12819,1191,1928,2678
 test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.4_postgresql_psycopg2_cextensions 5497,273,3577,11529,1077,1883,2439