【问题标题】:linux select performance scenarioslinux选择性能场景
【发布时间】:2012-12-05 21:39:57
【问题描述】:

我编写了一个简单的 tcp 服务器应用程序,其中用于读取的 fd_set 包括连接套接字描述符。服务器应用程序只要接收到消息,就简单地发送一个 ACK​​。客户端应用程序仅在收到来自服务器的 ACK 后才发送下一条消息。

// timeval == NULL
select(maxfd, &read_set, NULL, NULL, NULL)

当我这样做时,性能约为 3K 消息/秒。发送 ack 和收到客户端响应之间的延迟为 0.3ms。

// tm.tv_sec=0 and tm.tv_usec=0
select(maxfd, &read_set, NULL, NULL, tm)

但如果我这样做,性能会达到 8K 消息/秒,延迟会下降到 0.18 毫秒。

在后一种情况下,选择变成投票。有人可以解释为什么后一种情况比第一种情况好得多吗?

【问题讨论】:

  • 如果您担心延迟,请查看epoll。至于差异,它们是否一致?也许您可以阅读 libc/kernel 源代码以了解它的不同之处。
  • 就像工程中的所有事情一样,这是一种权衡。从系统的角度来看,“select (..., NULL)”在几乎所有情况下都是明显的胜利:它迅速响应;它不会占用系统资源。但是,在您的情况下,“选择(...,tm = 0)”表现更好。为什么? 1) 因为你有一个连续的网络流量流,并且 2) 你只关心响应网络。

标签: c linux tcp


【解决方案1】:

可能的答案

当超时为零时,当没有可用数据时,select() 调用会立即返回。这使您可以忙于等待轮询套接字,在数据到达之前主动消耗 CPU 周期。

当 timeout 为 NULL 时,如果没有数据,您的进程将进入 WAIT_INTERRUPTIBLE 状态等待数据可用。这会导致至少两次上下文切换的惩罚,一次离开您的进程,另一次在数据可用时返回。这样做的好处是你的进程放弃了 CPU 并允许其他进程运行。

这就像比较spinlocks and semaphores。自旋锁“旋转” CPU 等待条件,而信号量则产生 CPU。自旋锁的性能更高,但它们会占用 CPU,因此它们只能用于非常非常短的等待。信号量与其他进程更加合作,但由于额外的上下文切换,它们会产生明显的开销。

【讨论】:

  • 上下文切换的成本是几纳秒。我的66ns。给定每秒 8k 条消息,这不加起来
  • @Jimm 你如何测量上下文切换时间?谷歌搜索显示其他人测量上下文切换时间大约为 5-30us。
  • 我使用了github.com/tsuna/contextswitch/blob/master/timectxsw.c。你能分享我其他的链接吗,我可以试试看区别
  • 即使是 30 微秒,有 2 次上下文切换,即 60us,每次唤醒都应该额外产生 60us。在我的情况下,甚至醒来大约是 0.1 - 0.18 毫秒后
【解决方案2】:

它不会回答你的问题,但是如果你真的想要好的性能,并且如果收到的消息率很高,你可以试试:

  • 用 O_NONBLOCK 打开
  • 首先尝试阅读
  • 如果读取失败并出现错误代码 EAGAIN 或 EWOULDBLOCK,请使用 timeval == NULL 进行选择
  • 过程数据

【讨论】:

    【解决方案3】:

    select(2) 的手册页说

    timeout 是之前经过的时间量的上限 选择()返回。如果 timeval 结构的两个字段都为零, 然后 select() 立即返回。 (这对轮询很有用。)如果 timeout 为 NULL(无超时),select() 可以无限期阻塞

    强调的是我的。如果您担心延迟,您应该查看epoll(4)

    【讨论】:

    • 但大概read_set中的某些东西已经准备好了,所以这个“可以”并不能解释它。
    • 上限超时无关紧要。一旦服务器收到消息,select 将在上述两种情况下返回。
    猜你喜欢
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    相关资源
    最近更新 更多