我正在玩 C/Python 中的套接字,我想知道将标头从 Python 字典发送到客户端套接字的最有效方法是什么。
我的想法:
- 对每个标题使用
send
调用。优点:不需要内存分配。缺点:很多send
电话——可能容易出错;错误管理应该相当复杂 - 使用缓冲区。优点:一次
send
调用,错误检查更容易。缺点:需要一个缓冲区 :-)malloc
/realloc
应该相当慢,并且使用(太大)缓冲区以避免realloc
调用浪费内存。
对我有什么建议吗?谢谢 :-)
由于 TCP 拥塞控制的工作方式,一次发送所有数据的效率更高。TCP 维护一个窗口,它允许有多少数据“在空中”(已发送但尚未确认)。TCP 测量返回的确认以确定它可以“在空中”拥有多少数据而不会导致拥塞(即丢包)。如果应用程序没有足够的数据来填充窗口,TCP 就无法进行准确的测量,因此它会保守地缩小窗口。
如果您只有几个小的标头,并且您的调用send
快速连续,则操作系统通常会为您缓冲数据并将其全部发送到一个数据包中。在这种情况下,TCP 拥塞控制并不是真正的问题。但是,每次调用都send
涉及从用户模式到内核模式的上下文切换,这会产生 CPU 开销。换句话说,您最好还是在应用程序中进行缓冲。
有(至少)一种情况,如果没有缓冲你会更好:当你的缓冲区比上下文切换开销慢时。如果你在 Python 中编写一个复杂的缓冲区,那很可能就是这种情况。用 CPython 编写的缓冲区将比内核中经过精细优化的缓冲区慢很多。缓冲很可能会花费你比它买来的更多。
如有疑问,请测量。
不过要注意一点:过早的优化是万恶之源。这里的效率差异很小。如果您尚未确定这是您的应用程序的瓶颈,请选择让您的生活更轻松的任何方法。您可以随时更改它。
除非您要发送真正大量的数据,否则最好使用一个缓冲区。如果您使用几何级数来增加缓冲区大小,分配的数量将成为一个摊销常数,并且分配缓冲区的时间通常会随之而来。
调用意味着到内核(直接处理硬件的操作系统部分)的send()
往返。它的单位成本约为几百个时钟周期。除非您尝试调用send()
数百万次,否则这是无害的。
通常,缓冲send()
只是在收集到“足够的数据”时偶尔调用一次。“足够”并不意味着“整个消息”,而是类似于“足够的字节使得内核往返的单位成本相形见绌”。根据经验,8-kB 缓冲区(8192 字节)通常被认为是好的。
无论如何,对于所有与性能相关的问题,没有什么比实际衡量更重要了。尝试一下。大多数时候,没有任何实际的性能问题值得担心。