我非常怀疑从poll() 切换到epoll() 不会对您的应用程序的性能产生任何影响。 epoll() 的主要优点是当您有许多文件描述符(数百或数千)时,标准 poll() 需要在每次调用时完成更多工作,而 epoll() 会提前进行设置 - 只要由于您不更改正在观看的文件描述符集,因此每次调用都会稍微快一些。但一般来说,这种差异只有在很多很多文件描述符中才会明显。
请记住,如果您正在查看的文件描述符集非常频繁地更改,epoll() 的主要优势就会丧失,因为您仍然需要将新的文件描述符传递到内核中。因此,如果您要处理大量短期连接,那么切换到它就更没有吸引力了。
另一个区别是epoll() 可以是边缘触发,其中调用仅在描述符上发生new 活动或级别触发时返回,当描述符处于读/写就绪状态时调用返回。标准的poll() 调用总是 级别触发。然而,对于大多数人来说,级别触发是他们想要的——边缘触发接口有时很有用,但在大多数情况下,它们会导致数据在读取之后但在进入epoll() 调用之前到达套接字的竞争条件。我的建议是远离边缘触发的代码,除非你真的非常清楚自己在做什么。
您为 epoll() 付出的代价是缺乏可移植性 - poll() 和 select() 都是标准 POSIX 接口,因此使用它们您的代码将更加可移植。另一方面,epoll() 调用仅在 Linux 上可用。其他一些 Unix 变体也有自己的等效机制,例如 FreeBSD 上的 kqueue,但在这种情况下,您必须为每个平台编写不同的代码。
我的建议是,在您使用许多文件描述符之前,甚至不要担心epoll() - 说真的,几乎可以肯定,您的代码中还有很多其他地方可以实现更大的性能改进,而且完全有可能epoll() 对于您的用例可能不会更快。
如果您确实达到了处理许多连接的阶段并且您的其余代码已经非常优化,那么您应该首先考虑类似libev 的东西,它是一个跨平台接口,使用最佳性能调用每个特定的平台。它是performs very well,总体而言它可能比直接使用epoll() 更容易,即使您只想支持 Linux。
到目前为止,我还没有提到您提到的三个场景,因为我不相信它们中的任何一个对于少量文件描述符(例如 16)的执行情况都会有所不同。对于大量文件描述符,@987654340 @ 应该优于 poll(),尤其是在大多数空闲文件描述符的情况下。如果所有文件描述符始终处于活动状态,则两个调用都需要遍历每个连接来处理它。然而,随着空闲连接比例的增加,epoll() 提供了更好的性能,因为它只返回活动连接 - 使用 poll() 您仍然需要遍历所有内容,其中大部分将被跳过,但 epoll() 只返回您您需要处理的那些(最多可以指定的最大限制)。
明确说明(正如我上面提到的,这仅与大量连接相关):
- 大多数套接字都处于活动状态:两个调用大致相当,也许
epoll() 仍然稍微领先。
- 半活动半空闲:希望
epoll() 在这里会好一些。
- 大部分空闲:希望
epoll() 在这里肯定会更好。
编辑:
您可能希望看到来自 libevent 作者的this graph,它显示了随着文件描述符数量的变化处理事件的相对开销。请注意所有线是如何围绕原点收敛的,这表明所有机制都可以在少量描述符上实现相当的性能。