【问题标题】:How to speed up python networking?如何加速python网络?
【发布时间】:2014-06-08 13:50:40
【问题描述】:

我发现 python 网络

我有一个服务器(用 C 编写)。我用我的客户端(python)对其进行了测试。 我可以联系到2MB/s。这让我很担心,所以我检查了这个:

host1(客户端): cat some_big_file | nc host2 9999

host2(服务器): nc -l 0.0.0.0 9999 | pv > /dev/null

我达到了大约 120MB/s (1Gb) 的速度。

服务器不是瓶颈,我们在生产中使用它,它可以处理更多。但可以肯定的是,我复制了简单的 python gevent 服务器进行测试。它看起来像这样:

  #!/usr/bin/env python
  from gevent.server import StreamServer
  from gevent.pool import Pool

  def handle(socket, address):
       while True:
           print socket.recv(1024)

  pool = Pool(20000)
  server = StreamServer(('0.0.0.0', 9999), handle, spawn=pool)
  server.serve_forever()

下一个措施是从nc (host1) 发送到gserver (host2)

主机1:cat some_big_file | nc host2 9999 主机2:./gserver.py | pv > /dev/null

host2 上的输出:[ 101MB/s]。还不错。

但是,当我使用我的 python 客户端时,它仍然很慢。我将客户端切换到gevent。我尝试了几个greenlets。 1, 10, 100, 1000 - 它没有太大帮助,我可以用一个 python 进程达到20MB/s~30MB/s 2、3、4、5 个单独的 python 进程,这是一些东西,但仍然不是那么好)。还是很慢。我已经把客户端改成笨蛋了,像这样:

#!/usr/bin/env python
import sys
import socket

c = socket.create_connection((sys.argv[1], sys.argv[2]))
while 1:
        c.send('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n')

通过这种方法,我可以联系到10MB/s。我还尝试了将整个大 2GB 文件读取到内存并将其发送的方法,类似的结果。

我还尝试将 python 脚本作为单独的进程运行(使用tmux)。如果我使用 1 个进程,我可以达到 10MB/s,2 个进程 20MB/s,3 个进程 23MB/s,4、5、6 个进程没有改变任何东西(使用 gevent 版本和简单的版本进行测试)。

详情: Python-2.7.3 Debian 7 - 标准安装 机器是 AWS 实例,客户端是 c1.medium,服务器是 c3.xlarge。 nc 和 iperf 在机器之间测得 1Gb/s。

问题:

  1. 为什么我使用 python 服务器(gevent 服务器)可以快速接收大量数据,但即使 C 程序可以,也不能以相同的速度发送。
  2. 为什么将进程加倍不会将发送速度提高到极限,只会提高到某个值。
  3. 有什么方法可以在 python 中使用套接字快速发送数据?

【问题讨论】:

  • "I am finding python networking slow"。不,您发现 您的应用程序(恰好是用 Python 编写的)速度很慢。
  • @JonathonReinhart 是的,你是对的......但我尝试了不同的方法,因此有了这条捷径。

标签: python performance sockets python-2.7 network-programming


【解决方案1】:

问题并不在于网络速度很慢——python 函数调用有很多开销。如果您多次调用connection.send,您将在函数调用上浪费大量 CPU 时间。

在我的计算机上,您的程序平均约为 35 MB/s。做一个简单的修改,我得到 450 MB/s:

#...
c.send('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'*10+'\n')

通过一次发送更多数据,我可以达到超过 1GB/s 的速度。

如果您想最大化您的吞吐量,您应该在一次调用send 中发送尽可能多的数据。一种简单的方法是在发送最终结果之前连接几个字符串。如果你这样做,请记住 python 字符串是不可变的,因此连续的字符串连接(大字符串)很慢。您需要改用bytearray

【讨论】:

  • 即使你尝试在 C(甚至汇编)中一次发送几个字节的东西,它仍然会很慢,因为 syscall 开销会严重限制如何快速你可以将数据传递给内核。
  • @goncalopp,感谢您的精彩解释和 bytearray 提示。
  • 还有一点是Buffer Size,RTL8168 有8/16KB 的片上内存(如L1,L2,L3 缓存),从不通过Ethernet chips Internal Buff Size 发送数据包。使用 DMA 的以太网芯片不会释放整个内置内存。我认为最大尺寸是1600 byteData Interrupt 很难,需要小心设置你的通信数据模式。我达到了 40 微秒(发送+接收)数据包时间并使用了 2 个转换器(光纤到 CAT6A)。缓冲区大小很重要,因为所有芯片都有更多 ns(纳秒)的设置时间,也许小数据包更快,但您的芯片在发送+接收时有更多延迟。继续.. >>
  • Cont.. >> 你使用 big_packet 你需要 fast_RAM + Big_CPU_Cache + Stable_CPU + Stable_Bridge_CPU(像这样:低 FSB 对你有好处,需要将每一步时钟转换为总线级)。这不仅是 python 或数据模式问题,还包括系统的所有点(硬件、软件、电缆等)
  • 也许是 nagle 算法的问题? (programmersought.com/article/96935148609) 启用时,它会收集缓冲区中的数据并在缓冲区足以发送时触发它。当我在python上开发tcp代理时,它是问题的根源。试试吧,也许会有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-23
  • 2021-07-10
相关资源
最近更新 更多