2010-01-09 79 views
88

我所阅读和经历的一切(基于Tornado的应用程序)都让我相信,ePoll是基于Select和Poll的网络的自然替代品,特别是Twisted。这让我产生了偏执狂,这对于一种更好的技术或方法来说并不合适,因为它并不具备价格。Twisted选择/轮询与epoll反应器的注意事项

阅读epoll和替代品之间的几十个比较表明epoll显然是速度和可伸缩性的冠军,特别是它以线性方式缩放,这非常棒。这就是说,处理器和内存使用情况如何,epoll仍然是冠军?

回答

180

对于非常少量的套接字(当然,这取决于您的硬件,但我们正在谈论的东西大约在10或更少),选择可以在内存使用率和运行时速度上击败epoll。当然,对于这样少量的套接字,这两种机制都非常快,所以在绝大多数情况下,您并不关心这种差异。

虽然有一个解释。选择和epoll线性缩放。然而,一个很大的区别是面向用户空间的API具有基于不同事物的复杂性。调用select的成本大致与通过它的最高编号的文件描述符的值相同。如果你选择一个单一的fd,100,那么这个花费大约是选择一个单一的fd50的两倍。在最高点以下添加更多的fds不是很自由,所以在实践中它比这更复杂一点,但是这个对大多数实现来说是一个很好的第一个近似值

epoll的成本更接近实际上有事件的文件描述符的数量。如果您正在监视200个文件描述符,但其中只有100个文件描述符有事件发生,那么您(非常粗略地)仅支付这100个活动文件描述符。这是epoll倾向于提供超过选择的主要优点之一。如果你有一千个大多数闲置的客户,那么当你使用select的时候,你仍然在为所有的客户付费。然而,对于epoll来说,就像你只有几个 - 你只支付那些在任何特定时间都处于活动状态的人。

所有这一切意味着epoll将导致大多数工作负载的CPU使用率降低。就内存使用情况而言,这是一个折腾。 select确实设法以高度紧凑的方式表示所有必要的信息(每个文件描述符一位)。 FD_SETSIZE(通常为1024)限制您可以与select一起使用多少个文​​件描述符,这意味着您将永远不会为可用于select(读取,写入,例外)的三个fd集中的每一个花费超过128个字节。相比于那些384字节的最大值,epoll就像是一头猪。每个文件描述符都由多字节结构表示。但绝对而言,它仍不会占用太多内存。你可以用几十千字节表示大量的文件描述符(我认为大概每1000个文件描述符有20k)。如果你只想监视一个文件描述符,但是它的值恰好是1024,那么你必须花费所有这些字节的384个字节与select这一事实,而你只花费20字节。尽管如此,所有这些数字都很小,所以它没有太大的区别。

还有epoll的其他好处,也许你已经意识到,它不限于FD_SETSIZE文件描述符。您可以使用它来监视尽可能多的文件描述符。如果你只有一个文件描述符,但它的值大于FD_SETSIZE,epoll也可以使用,但select不会。

随机地,我最近还发现了epollselectpoll相比有一点小缺点。虽然这三个API都不支持普通文件(即文件系统上的文件),但是selectpoll表示缺乏支持,因为报告的这些描述符始终可读且始终可写。这使它们不适用于任何有意义的非阻塞文件系统I/O,这是一个使用selectpoll的程序,碰巧遇到来自文件系统的文件描述符将至少继续运行(或者如果失败,它不会是因为selectpoll),尽管它可能不是最佳性能。

另一方面,当要求监视这样的文件描述符时,epoll将快速失败并显示错误(EPERM,显然)。严格来说,这很难。这只是表明它缺乏明确的支持。通常我会赞扬明确的失败条件,但是这个没有记录(据我所知),并导致完全破坏的应用程序,而不是仅仅以可能降级的性能运行的应用程序。

实际上,我见过的唯一地方就是与stdio互动。用户可能会将标准输入或标准输出从/重定向到普通文件。而之前的stdin和stdout将会是一个由epoll支持的管道 - 然后它变成了一个普通的文件,epoll会大声地失败,从而破坏应用程序。

+0

非常好的答案。考虑清楚“poll”的完整性行为? – quark 2010-06-15 21:39:45

+6

我从普通文件读取行为的两分钱:我通常更喜欢直接失败的性能下降。原因是它在开发过程中被检测到的可能性更大,因此可以正确解决问题(比如通过为实际文件执行I/O的替代方法)。当然YMMV:在这种情况下,失败并不会更好。但是,仅在特殊情况下才会发生的急剧放缓在开发过程中可能很难捕捉到,在实际部署时将其留作定时炸弹。 – quark 2010-06-15 21:42:36

+1

刚刚完全阅读您的编辑。从某种意义上说,我确实认为它可能不适合模仿它的前辈,但我可以再次想象实施EPERM错误的开发者认为“仅仅因为它总是被打破了,并不能把它当作我的好。”另一个反驳的论点是,我是一名防守型程序员,任何超过1 + 1的人都是可疑的,我以这种方式进行编码以允许优雅的失败。让内核因期望错误而失败并不好或体贴。 – David 2010-11-07 05:37:41

3

在我公司的测试中,epoll()的一个问题出现了,因此与select相比单个成本。

当尝试从超时网络读取数据时,创建epoll_fd(而不是FD_SET)并将fd添加到epoll_fd,比创建FD_SET(这是一个简单的malloc)要昂贵得多。

根据之前的答案,随着过程中FD的数量变大,select()的成本变高,但在我们的测试中,即使在10,000的fd值中,select依然是赢家。这些情况下只有一个线程正在等待的fd,并且只是试图克服网络读取和网络写入在使用阻塞线程模型时不超时这一事实。当然,与非阻塞反应堆系统相比,阻塞螺纹模型的性能较差,但有些情况下需要与特定的遗留代码库集成。

这种用例在高性能应用程序中很少见,因为反应器模型不需要每次都创建一个新的epoll_fd。对于epoll_fd使用寿命长的模型---这对任何高性能服务器设计来说都是明显的首选--- epoll在各方面都是明显的赢家。

+5

但是如果在10k +范围内有文件描述符值,你甚至不能使用'select()' - 除非你重新编译一半系统来改变FD_SETSIZE - 所以我想知道这个策略是如何工作的。对于你描述的场景,我可能会看'poll()'更像'select()',而不像'epoll()' - 但删除了FD_SETSIZE限制。 – 2014-04-30 12:07:55

+0

因为您可以使用malloc()FD_SET,所以如果文件描述符值在10K范围内,则可以使用select()。实际上,由于FD_SETSIZE是编译时间,并且实际的fd限制是在运行时,所以FD_SET的安全使用会根据FD_SET的大小检查文件描述符的编号,并且如果FD_SET是(),则执行malloc(或道德等价)太小。 当我在客户的生产中看到这一点时,我感到震惊。在编写套接字20年之后,我写过的所有代码以及Web上的大多数教程都是不安全的。 – 2014-05-07 19:01:34

+5

据我所知,在任何流行的平台上都不是这样。 'FD_SETSIZE'是编译你的* C *库时设置的编译时间常量。如果您在构建应用程序时将其定义为不同的值,那么您的应用程序和C库将不同意,并且事情将变得糟糕。如果你有引用声明重新定义'FD_SETSIZE'是安全的,我很乐意看到它们。 – 2014-05-07 20:00:42