]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix quadratic performance in gen.engine when yielding a large list.
authorBen Darnell <ben@bendarnell.com>
Tue, 12 Feb 2013 00:13:25 +0000 (19:13 -0500)
committerBen Darnell <ben@bendarnell.com>
Tue, 12 Feb 2013 00:13:25 +0000 (19:13 -0500)
tornado/gen.py
tornado/test/gen_test.py

index e309bf3c0a08897712f079fd61e3df933da38c8e..8d9d8935cbe99e974c03bfcfc60b80f868d8abfa 100644 (file)
@@ -66,6 +66,7 @@ from __future__ import absolute_import, division, print_function, with_statement
 
 import collections
 import functools
+import itertools
 import sys
 import types
 
@@ -282,13 +283,17 @@ class Multi(YieldPoint):
                 i = YieldFuture(i)
             self.children.append(i)
         assert all(isinstance(i, YieldPoint) for i in self.children)
+        self.unfinished_children = set(self.children)
 
     def start(self, runner):
         for i in self.children:
             i.start(runner)
 
     def is_ready(self):
-        return all(i.is_ready() for i in self.children)
+        finished = list(itertools.takewhile(
+                lambda i: i.is_ready(), self.unfinished_children))
+        self.unfinished_children.difference_update(finished)
+        return not self.unfinished_children
 
     def get_result(self):
         return [i.get_result() for i in self.children]
index ad5310776a185124af1143dca4dfa9ed1d8dc392..7826bb6a8205f6bc58a70afc19579e361ee07bd7 100644 (file)
@@ -1,6 +1,8 @@
 from __future__ import absolute_import, division, print_function, with_statement
 
 import functools
+import time
+
 from tornado.concurrent import return_future
 from tornado.escape import url_escape
 from tornado.httpclient import AsyncHTTPClient
@@ -227,6 +229,16 @@ class GenTest(AsyncTestCase):
             self.stop()
         self.run_gen(f)
 
+    @gen_test
+    def test_multi_performance(self):
+        # Yielding a list used to have quadratic performance; make
+        # sure a large list stays reasonable.  On my laptop a list of
+        # 2000 used to take 1.8s, now it takes 0.12.
+        start = time.time()
+        yield [gen.Task(self.io_loop.add_callback) for i in range(2000)]
+        end = time.time()
+        self.assertLess(end - start, 1.0)
+
     @gen_test
     def test_future(self):
         result = yield self.async_future(1)