【发布时间】:2011-08-19 20:56:30
【问题描述】:
我正在开发一个允许在单台机器上模拟网络的程序。为此,我将 Twisted 用于异步 I/O,因为每个“连接”都有一个线程可能有点多。 (我还使用他们的 NIO 在 Java 中实现了一个类似的程序)。然而,当我扩大模拟网络规模时,Twisted 的吞吐量会下降。将此与 Java 实现进行比较时,对于相同的网络规模,Java 吞吐量继续增长。 (增长速度放缓,但仍在增长)。 例如。 (Python 100 个节点 = 58MB 总吞吐量,300 个节点 = 45MB,Java 100 个节点 = 24 MB,300 个节点 = 56MB)。
我想知道是否有人对为什么会发生这种情况有任何建议?
我能想到的唯一原因是 Java 的每个“对等点”都在自己的线程中运行(该线程包含自己的选择器,用于监视对等点的连接)。在 python 版本中,所有内容都注册到反应器(以及随后的一个选择器)。随着 python 的扩大,一个选择器无法快速响应。不过这只是猜测,如果有人有更多具体信息,将不胜感激。
编辑: 我按照 Jean-Paul Calderone 的建议进行了一些测试,结果发布在 imgur。对于那些可能想知道测试报告的以下平均吞吐量的人。 (分析是用 cProfile 完成的,测试运行了 60 秒)
Epoll 反应器: 100 个对等点:20.34 MB,200 个对等点:18.84 MB,300 个对等点:17.4 MB
选择反应堆: 100 个对等点:18.86 MB,200 个对等点:19.08 MB,300 个对等点:16.732 MB
似乎与报告的吞吐量有关的几件事是对 main.py:48(send) 的调用,但这种相关性并不令人惊讶,因为这是发送数据的地方。
对于这两个反应器,在套接字上发送函数所花费的时间随着吞吐量的降低而增加,并且对发送函数的调用次数随着吞吐量的降低而减少。 (即:在套接字上发送的时间更多,在套接字上发送的调用更少。) epoll {method 'send' of '_socket.socket' objects} 在 100 个对等点上进行 413600 次调用需要 2.5 秒,对于 300 个对等点上的 epoll 需要 5.5 秒,对于 354300 次调用。
为了尝试回答最初的问题,这些数据是否似乎表明选择器是一个限制因素?在选择器中花费的时间似乎随着对等点数量的增加而减少(如果选择器使一切变慢,难道不会期望在选择器中花费的时间会增加吗?)还有什么其他可能会减慢正在发送的数据量? (数据的发送只是对reactor.calllater一次又一次注册的每个peer的一个函数,就是main.py:49(send))
【问题讨论】:
-
如果您尽可能使用
deferToThread(即使在回调和 errbacks 上显式),那么您可能会遇到 Twisted 甚至 CPython 的限制。您是否尝试过提高 Twisted 线程池大小,或者在 PyPy 或 Stackless Python 下运行? -
另一件在高端可能很重要的是您正在使用的反应器。默认情况下,Twisted 在 Unix 上使用
SelectReactor。提供了其他反应器类别。我从来没有使用过任何其他的。使用不同的反应器不是一个常见的用例,但可能有一个专为高度线程设计的。 -
@wberry:你是对的,正如 Jean-Paul 建议使用
epoll反应器。检查他的链接。 -
@wberry 我没有在任何地方使用 deferToThread,因为没有做太多繁重的工作,(只是发送和一些小计算。)我确实尝试了 epoll,一件事我发现它的缩放更好一些,但更有趣的是它在吞吐量中似乎更稳定一些(测量之间的变化较小)。请参阅上面编辑中提供的链接。
标签: java python twisted selector throughput