【问题标题】:Why exactly does ePoll scale better than Poll?为什么 ePoll 的规模比 Poll 更好?
【发布时间】:2011-03-21 21:28:14
【问题描述】:

简短的问题,但对我来说很难理解。

为什么 ePoll 的扩展性比 Poll 更好?

【问题讨论】:

    标签: epoll


    【解决方案1】:

    虽然 Damon 的原因对于您从不阻塞套接字的异常情况是正确的,但在典型的实际程序中,原因完全不同。一个典型的程序如下所示:

    1) 做我们现在能做的所有工作。

    2) 检查是否有网络连接需要服务,无事则阻塞。

    3) 为发现的任何网络连接提供服务。

    4) 转到步骤 1。

    通常,因为您只是完成了所有可以做的工作,所以当您回到第 2 步时,您就没有工作要做了。所以你需要稍等片刻。现在,假设有 800 个您感兴趣的套接字。内核必须为这 800 个套接字中的每一个放入等待队列。而且,一瞬间,当数据到达这 800 个套接字中的一个时,内核必须将您从这 800 个等待队列中删除。将任务放在等待队列上需要创建一个“thunk”以将该任务链接到该等待队列。没有好的优化是不可能的,因为内核不知道你将等待哪个 800 个套接字。

    使用epoll,epoll 套接字本身有一个等待队列,进程只被放在一个等待队列中。需要一个 thunk 将 800 个连接中的每一个连接到 epoll 等待队列,但该 thunk 是持久的。您可以通过将套接字添加到 epoll 集合来创建它,并且它会一直保留在那里,直到您从集合中移除该套接字。

    当套接字上有活动时,内核会在检测到活动的任务中处理它。当您等待时,内核已经知道是否检测到事件并且内核只需要将您放在那个等待队列中。当你醒来时,它只需要将你从那个队列中移除。

    因此,selectpoll 的杀手锏与其说是复制,不如说是内核必须在每次阻塞操作时操纵大量等待队列。

    【讨论】:

    • 感谢您的精彩文章。一个简单的问题:假设系统上有 4000 个进程,每个进程处理大约 5 个文件描述符 (FD),如果使用 epoll 代替 poll,是否会提高整体系统性能?阅读您关于选择/轮询所涉及的内核开销的文章,我猜会有..
    • 我突然想到每个进程的epoll socket会不同,进程的FD不会有一个等待队列。在这种情况下我使用 poll 或 epoll 有关系吗?
    • 每个进程都有自己的 epoll 描述符和自己的等待队列。这意味着每次必须阻止进程等待 I/O 时,只需将其放置在一个等待队列上而不是大约五个,并且在唤醒时只从一个等待队列中移除而不是大约五个。这是否重要取决于 I/O 负载,但肯定会更好。
    • 感谢您的快速回复。
    【解决方案2】:

    poll 系统调用每次都需要将文件描述符列表复制到内核。 epoll_ctl 只会发生一次,但不是每次调用 epoll_wait 时都会发生这种情况。

    另外,epoll_waitO(1) 观察描述符的数量1,这意味着无论您等待一个描述符还是等待 5,000 或 50,000 个描述符都没有关系。 poll,虽然比 select 更有效,但每次仍然需要遍历列表(即,就描述符数量而言,它是 O(N))。

    最后,除了“正常”模式之外,epoll 还可以在“边缘触发”模式下工作,这意味着内核不需要在收到准备就绪信号后跟踪您读取了多少数据。这种模式更难掌握,但效率更高。


    1正如 David Schwartz 正确指出的那样,epoll_wait 对于发生的事件当然仍然是 O(N)。几乎没有任何方法可以与任何界面有所不同。如果 N 个事件发生在被监视的描述符上,那么应用程序需要获得 N 个通知,并且需要在为了对正在发生的事情做出反应。
    这在边沿触发模式中再次略有不同,但没有根本不同,在这种模式下,您实际上会通过M <= N 获得M 事件。在边缘触发模式下,当同一事件(例如,POLLIN)发生多次时,您可能会收到较少的通知,可能只有一个。但是,这对于 big-O 符号本身并没有太大变化。

    但是,epoll_wait观看的描述符数量无关。假设它以预期的“正常”方式使用(即,许多描述符,很少的事件),这才是真正重要的,这里确实是O(1)

    作为一个类比,您可以想到一个哈希表。哈希表在O(1) 中访问其内容,但有人可能会争辩说计算哈希 实际上是O(N) 的密钥长度。这在技术上是绝对正确的,并且可能存在这样的问题,但是,对于大多数人来说,这无关紧要。

    【讨论】:

    • 感谢您的回答。但是,如果使用 epoll,我只需要复制一次文件描述符,我该如何从用户空间更改文件描述符列表?
    • 您使用 epoll_ctl 在集合中添加/删除/更改描述符到/从/中。这会根据您的请求更改内核结构(例如,它将 eventpoll 结构添加到文件描述符的等待队列中)。当你调用 epoll_wait 时,它只是阻塞。当文件描述符上发生某些事情时,它会唤醒该描述符的等待队列(实际上,它比这更复杂,在 eventpoll 结构中还有另一个队列,因此多个线程可以阻塞在同一个 fd 上,但基本上就是这样)。所以实际上它是一个“有针对性的通知”而不是“搜索投票”,这就是为什么它也是 O(1)。
    • 实际上,epoll_wait 是 O(N),而不是 O(1)。 epoll_wait 发现的套接字数量为 O(N),就像 poll 一样。每个发现的套接字都必须复制到应用程序的epoll_event 结构中。如果将轮询的套接字数量增加一倍,那么预期有活动的套接字数量就会增加一倍。
    • @DavidSchwartz:您在某种程度上是正确的,epoll_wait 同时是O(N)O(1)。但重要的是它是O(1)为它设计的“正常”场景。假设您观察到许多(数十、数百、数千)描述符,而实际上一次发生的事件很少(通常少于 5 个)。 epoll 不能做“魔术”,就发生的事件而言,它不是(也不能是)O(1) - 如果发生 N 个事件,那么 something 必须为 N 个事件完成。但是,如果 N ≈ 1,则实际上与 O(1) 一样好。更重要的是...
    • ...就描述符的数量而言,它是O(1)。因为如果你有 N ≈ 10000,那么 O(1)O(N) 就不是一回事了(很明显)。在这里,真的,真的很重要。您的反对意见是正确的,如果您以一种与预期用途相反的方式使用 epoll 接口并且期望“魔术”发生,那么您可能会感到失望。在这种情况下,普通的 poll 可能既容易又快。我在 this lengthy answer 中提到了这一点,其中谈到了 epoll 等(大约在中间)。
    猜你喜欢
    • 2011-05-04
    • 2013-06-25
    • 2013-08-03
    • 2020-04-27
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多