我在最后几天经历过这个麻烦,我似乎无法完全理解龙卷风基因库。
我有这段代码,例如:
@gen.coroutine
def get(self, build_id=None):
status_query = self.get_query_arguments("status")
limit_query = self.get_query_arguments("limit")
results = [self._dummy() for i in range(15)]
yield results
def _dummy(self):
http_client = tornado.httpclient.AsyncHTTPClient()
return http_client.fetch("https://www.google.com", headers=self.headers, validate_cert=False)
正如我想的那样,我提出的15个谷歌请求应该几乎同时触发。 "结果"列表,应该是一个期货列表,然后,产生列表应该等待所有这些列表完成。
实际上已经发生了,但它需要大约6秒才能完成这些请求,而且随着我增加for循环的范围,它会逐渐增长。
他们不应该在同一时间准备好吗?
我错过了什么吗?
非常感谢!
答案 0 :(得分:2)
AsyncHTTPClient的默认max_clients为10.当您发起15个请求时,其中10个立即开始,但其余5个必须等待其他请求完成才能开始。要开始更多并发请求,请将max_clients提升到更大的数字。 See Tornado's documentation for details on configuring AsyncHTTPClient.
答案 1 :(得分:1)
如果您的请求不是IO绑定,那么您将看不到太多变化。 - Me
在编程中,这些是我们的主要限制:
在Python中,由于GIL,我们在CPU访问方面甚至受到进一步的限制。随着现代计算机倾向于多核--2,4,8或16 - 我们甚至进一步削弱了 1 ,因为通常每个那些处理器会慢一点。有关GIL的更多信息,请查看David Beazley's GIL talk和Larry Hasting's GIL-ectomy。
为了绕过Global Interpreter Lock,开发了几个回调式模块,如Twisted,Tornado和asyncio。这些工作的方式是通过执行某些操作,当它们到达IO停止点时通常会产生控制。
例如,如果我将数据写入旋转磁盘,也许我可以将100kb写入磁盘,但是当我等待写入所有这些信息时,也许我可以进行1000次计算在所有数据完成之前。
或者,也许我可以每秒向Web服务发出100个请求,但是每个请求只执行我的计算需要0.0001秒。如果你看一下我花时间在哪里的图表,它会看起来像这样:
#
#
#
#
#
#
#
# #
--------------------------
reading processing
这些进程允许你做的是交错处理和读/写,通过发送请求数据包,然后做其他事情,然后在某些时候回来读取返回的数据包。
像这样的IO绑定,你可以看到相当大的加速,因为而不是看起来像这样:
start end start end
--|--------|----|--------|------
t=0 t=5 t=6 t=11
你可以得到这样的东西:
start end
start| end |
--|---|------|---|-
t=0 t=1 t=5 t=6
但如果您的进程受CPU限制,您将不会看到任何加速(或至少不会太多),因为您花费30多秒进行处理而只有1s做任何类型等待网络。
在尝试异步方法之前,先给出标准的单线程方法,然后看1)它是否足够快,2)是否在网络/ IO边界处慢。
您可以轻松地使用像line profiler这样的Python,并且(如果还没有)将您的读取,处理和写入功能分开,并查看您在哪里花时间。如果你大部分时间都花在阅读功能上,那么你应该从异步方法中看到一个相当合理的加速。如果没有,异步只会让你失望。
1 老实说,这并不是那么糟糕,除非你有超速的关键。然后你应该使用cffi或其他东西来获取速度关键部分并将它们转储到C.你做了找出哪些部分是阻止,对吗?