首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python3.6: socket.recv() vs socket.recv_into()性能

python3.6: socket.recv() vs socket.recv_into()性能
EN

Stack Overflow用户
提问于 2021-01-13 10:13:05
回答 1查看 253关注 0票数 1

我一直在使用python3.6捕获高速udp流,并尝试使用socket.recv()socket.recv_into()。我预计recv_into()会更快,因为每次读取数据包并将其添加到列表中时,它都会直接复制到"preallocatedbytearray中,而不是creating a new string中。

我的测试场景是核心受限的,我知道我正在丢弃一些数据包,并且通过SO_RCVBUF上的setsockopt拥有很大的套接字接收缓冲区大小。我还关闭了垃圾收集器,以避免随机中断。

下面的代码片段具有类似的性能,这对我来说没有任何意义,我想知道是否有人可以帮助指出我遗漏了什么。谢谢!

代码语言:javascript
复制
pkts = []
while time.time() - t_start < 10.0:
    pkt = s.recv(2048)
    pkts.append(pkt)
num_recv_captured = len(pkts)

代码语言:javascript
复制
buffer = bytearray(2048)

num_recv_into_captured = 0
while time.time() - t_start < 10.0:
    s.recv_into(buffer, 2048)
    num_recv_into_captured += 1

在这里,我看到num_recv_into_captured在核心绑定场景中类似于num_recv_captured,但预期num_recv_into_captured会更大一些。

EN

回答 1

Stack Overflow用户

发布于 2021-01-13 10:52:47

性能测量是非常困难的。您所看到的可能是由于您的测试方法的问题,或者可能是结果太接近而无法察觉。

因此,首先看一下您尝试比较的两种方法。您可能会认为,唯一的区别是第二个不需要分配新的缓冲区,这是一个真正的不同,是关键有意义的一个,但不是唯一的一个。如果这是唯一的区别,你会期望它可靠地更快,但这不是唯一的区别。第二个方法还接受Python需要解析和处理的额外的动态鸭子类型参数。这应该不会花费太多时间,但很难说它与分配2048字节的时间相比如何,这将取决于解释器使用的方法。Python使用一个全局内存池,并且在一个紧凑的循环中,它可能会一次又一次地释放和重新分配相同的内存,而不需要调用任何OS函数。

这就引出了下一个问题,虽然很难确定这两个操作的成本有多高(也许其他人更清楚它们中的任何一个有多有意义),但它们的规模与网络通信的规模不太一样。您现在看到的是nano/micro第二风格的性能差异,因为它们与毫秒风格的网络操作相关。您不仅调用操作系统并等待IO,而且在接收数据的速度比发送数据的速度快的情况下,您的进程很可能会被操作系统休眠,特别是当您确实受到内核限制的时候。您还提到了数据包丢失,这不一定是确定性的。

如果您真的关心这种性能级别,那么您应该使用C/C++或Rust或其他允许您进行较低级别访问的语言,或者编写一个C/C++或Cython模块,并通过使用该模块的python直接使用C套接字库(如果您的目标平台是linux,您甚至可以使用recvmmsg来真正提高性能)。但你很可能不会。我不会为了实验而反对实验(实际上,当你问这样的问题时,我觉得很烦人,互联网上的人只是向你解释为什么不麻烦,因为你不需要它或其他什么),所以如果是这样的话,你应该学到的是,通常微优化几乎没有什么不同。

如果您正在尝试决定在较大的项目中使用哪种方法;如果您有任何理由为了方便起见而选择其中一种方法,请使用该方法。如果你真的关心性能,我会坚持使用recv_into。即使调用速度并不比recv快。如果你有一个有意义的应用程序调用这个方法,它的内存特性就会发挥作用,我希望整个系统在没有所有非常小的分配和释放的情况下会运行得更好,这些分配和释放不太可能像你的小基准测试循环中那样完美地排列在一起。

编辑:只是为了清楚,在这种情况下,数据包丢失并不是确定性的,因为系统上正在进行的其他操作没有被准确地记录和复制。我想说,它在理论上总是确定性的,但作为一个观察者,它实际上是不可知的。

编辑2:我突然想到你提到禁用垃圾收集。这只会禁用收集器,但基于引用计数的内存释放应该仍然会发生,因此可能是紧密的recv循环一次又一次地释放和重新分配相同的内存块,由于它是由CPython而不是操作系统分配的,并且是少量内存,因此很可能很快就能完成。

编辑3:很晚了.无论如何,我只是注意到你在recv下添加了所有的包到一个列表中,这样你就不会释放和重新分配内存,你只需要让它们保持原样,并将内存地址存储在列表结构中,这应该是一个非常快的操作。不释放内存意味着你不会重新使用相同的地址,但这也意味着不需要进行释放分配,并且与返回操作系统并填充缓冲区相比,分配额外的2048字节块仍然非常快。与任何操作系统建立的进程休眠相比,这些操作也会相形见绌。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65694746

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档