From: Ben Darnell Date: Tue, 12 Feb 2013 00:13:25 +0000 (-0500) Subject: Fix quadratic performance in gen.engine when yielding a large list. X-Git-Tag: v3.0.0~131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=da5c2be036f95b2a380ea520ff83b0186e329102;p=thirdparty%2Ftornado.git Fix quadratic performance in gen.engine when yielding a large list. --- diff --git a/tornado/gen.py b/tornado/gen.py index e309bf3c0..8d9d8935c 100644 --- a/tornado/gen.py +++ b/tornado/gen.py @@ -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] diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py index ad5310776..7826bb6a8 100644 --- a/tornado/test/gen_test.py +++ b/tornado/test/gen_test.py @@ -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)