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