【发布时间】:2012-02-10 22:17:16
【问题描述】:
考虑一个每秒有数千个请求的缓存应用程序,其中客户端使用整数索引请求对象,服务器将对象发送回给它。 (仅限 Linux)
对于 UDP 协议来说,什么会更快:
- 在一个数据包中发送多个回复?
- 在一个数据包中发送一个回复?
(适用于双方,服务器或客户端)
缺点:
-
每个数据包有多个回复:
- 将需要许多 memcpy() 来组装具有多个回复的数据包。如果 get() 请求向客户端返回 16 字节对象,则在发出 sendto() 系统调用之前,一个 65535 字节的单个数据包必须 memcpy() 大约 4000 个对象。 memcpy() 开销很大
- 一个 65000 字节的 UDP 包会产生大约 43 个 udp 包头 因为数据包将在 Fragmetns 中传输,因为默认 MTU 大小为 1500。 开销不大
- 每个数据包单个回复:
- 每个 get() 都会产生 28 字节的协议开销。对于 16 字节的对象,这将是昂贵的。每秒对象的吞吐量将很低,网络将被数据包饱和。例如,发送 4000 个对象将生成 4000 个 udp 数据包标头,比具有多个回复的 43 个标头要多得多。
- 此外,过多的 sendto() 系统调用会增加 CPU 消耗,因为内核必须在每个系统调用上保存 CPU 寄存器。
似乎这两种方法都有其缺点,尚不清楚哪种解决方案会更好,但是,千兆以太网的时钟运行在 1GHz,而处理器是例如 3GHz,内存也是 总线比以太网接口的双绞线宽得多。所以多回复 每个数据包会是更好的选择,因为额外的 memcpy() 会减少网络流量,对吗?
现在,如果我们使用 TCP 而不是 UDP:
- 每个数据包有多个回复:
- 再次,将需要许多 memcpy() 来组装数据包,该数据包不应大于 65535,然后内核会将其分段以 MTU 大小的数据包发送它与这里有 UDP
- 每个数据包单个回复:
- 由于我们的 16 字节对象随机分布在服务器的内存中,我们必须为每个对象发出一个 write() 系统调用,系统调用的数量与 udp 的单个请求情况相同。
- 每个 get() 都会导致 52 字节的开销,因为 TCP 具有更大的标头 + 我们将不得不另外一个大约 40 字节的 ACK 数据包开销。 更大的网络流量
结论:
- 对于这个特定的应用程序(从客户端给定的索引向客户端发送简单的数据块)TCP 的性能不会比 UDP 更好。 + 考虑到 TCP 栈的实现比 UDP 复杂得多,在那里执行的指令更多。 使协议运行得更快的关键在于将 UDP 数据包组装到 MTU 中尽可能大。
- 向系统添加另一个 NIC 的成本低于添加另一个 CPU。减少 memcpy() 的数量是有意义的,但发送一堆小的 UDP 数据包并为网络端每个 udp 标头的开销支付额外费用(+ 通过执行许多 sendto() 系统调用来增加一些开销我认为会低于 memcpy() 的开销)因为这种方式使用一个 CPU 可以通过多个 NIC 发送请求
非常感谢您的 cmets 和您在协议选择方面的经验。
注意: 让我们抛开可靠性问题,假设我们的网络没有错误,即 如果您正确连接它们,大多数情况下都是正确的,如果发生错误,那肯定不是常态
【问题讨论】:
-
我的解决方法是将数据包保持在 MTU 下,并在每个数据包中批处理一定数量的结果以及识别信息,即 2 of 10 或其他方式以确保您没有丢失任何事物。请记住,UDP 允许丢失数据包并且不会自行重试,而 TCP 将尝试重新发送未确认的数据包。从您的可靠性声明来看,我认为您控制了网络并且没有通过任何第三方/互联网设备?
-
即使在一个健壮的内部网络上,在 UDP 上可能没有严重的数据“损坏”问题,如果有多个路由或多个 NIC,数据包偶尔会在 UDP 中重新排序并不少见发送端或接收端。最终,您的协议/应用程序对丢包、损坏和重新排序是否稳健?
-
即使网络没有错误(哈哈!),交换机、路由器、网卡、操作系统也会丢弃数据包。不需要那么大的 UDP 数据包突发来填满缓冲区并导致数据包丢失。
-
好吧,如果发生丢包,客户端将在超时后重新发送。但既然这不应该发生,为什么要打扰。我在数据中心拥有的最好的服务器有 5 年的正常运行时间,并且只有大约 300 个数据包在其 /sbin/ifconfig 输出中出现错误
-
我发现这篇文章multicorepacketprocessing.com/tag/udp 说 TCP 比 UDP 快,但这是不正确的,因为使用 UDP 您还可以发送大小为 15000 字节的数据包,因此系统调用的数量将是相同的。读起来很有趣
标签: c linux networking network-programming