Para cantidades muy pequeñas de sockets (varía dependiendo de su hardware, por supuesto, pero estamos hablando de algo del orden de 10 o menos), select puede vencer a epoll en uso de memoria y velocidad de ejecución. Por supuesto, para un número tan pequeño de sockets, ambos mecanismos son tan rápidos que realmente no le importa esta diferencia en la gran mayoría de los casos.
Sin embargo, una aclaración. Ambos seleccionan y escalan linealmente. Sin embargo, una gran diferencia es que las API orientadas al espacio de usuario tienen complejidades que se basan en cosas diferentes. El costo de una select
llamada va aproximadamente con el valor del descriptor de archivo con el número más alto que le pasa. Si selecciona en un solo fd, 100, entonces eso es aproximadamente el doble de caro que seleccionar en un solo fd, 50. Agregar más fds por debajo del más alto no es del todo gratis, por lo que es un poco más complicado que esto en la práctica, pero esto es una buena primera aproximación para la mayoría de las implementaciones.
El costo de epoll está más cerca de la cantidad de descriptores de archivo que realmente tienen eventos en ellos. Si está monitoreando 200 descriptores de archivos, pero solo 100 de ellos tienen eventos, entonces (muy aproximadamente) solo está pagando por esos 100 descriptores de archivos activos. Aquí es donde epoll tiende a ofrecer una de sus principales ventajas sobre select. Si tiene mil clientes que en su mayoría están inactivos, entonces cuando usa Select todavía está pagando por los mil. Sin embargo, con epoll, es como si solo tuviera unos pocos: solo paga por los que están activos en un momento dado.
Todo esto significa que epoll conducirá a un menor uso de CPU para la mayoría de las cargas de trabajo. En lo que respecta al uso de la memoria, es un poco complicado. select
logra representar toda la información necesaria de una manera muy compacta (un bit por descriptor de archivo). Y la limitación FD_SETSIZE (típicamente 1024) sobre la cantidad de descriptores de archivo que puede usar select
significa que nunca gastará más de 128 bytes para cada uno de los tres conjuntos fd que puede usar conselect
(lectura, escritura, excepción). Comparado con esos 384 bytes como máximo, epoll es una especie de cerdo. Cada descriptor de archivo está representado por una estructura de varios bytes. Sin embargo, en términos absolutos, todavía no utilizará mucha memoria. Puede representar una gran cantidad de descriptores de archivo en unas pocas docenas de kilobytes (aproximadamente 20k por cada 1000 descriptores de archivo, creo). Y también puede agregar el hecho de que tiene que gastar los 384 de esos bytes select
si solo desea monitorear un descriptor de archivo, pero su valor es 1024, mientras que con epoll solo gastaría 20 bytes. Aún así, todos estos números son bastante pequeños, por lo que no hace mucha diferencia.
Y también existe otro beneficio de epoll, que quizás ya conozca, que no se limita a los descriptores de archivo FD_SETSIZE. Puede usarlo para monitorear tantos descriptores de archivos como tenga. Y si solo tiene un descriptor de archivo, pero su valor es mayor que FD_SETSIZE, epoll también trabaja con eso, pero select
no lo hace.
Al azar, también he descubierto recientemente un pequeño inconveniente en epoll
comparación con select
o poll
. Si bien ninguna de estas tres API admite archivos normales (es decir, archivos en un sistema de archivos), select
y poll
presentan esta falta de soporte como reportando descriptores como siempre legibles y escribibles siempre. Esto los hace inadecuados para cualquier tipo significativo de E / S del sistema de archivos sin bloqueo, un programa que usa select
o poll
y se encuentra con un descriptor de archivo del sistema de archivos al menos continuará funcionando (o si falla, no será porque de select
o poll
), aunque quizás no con el mejor rendimiento.
Por otro lado, epoll
fallará rápidamente con un error ( EPERM
aparentemente) cuando se le solicite monitorear dicho descriptor de archivo. Estrictamente hablando, esto no es incorrecto. Simplemente indica su falta de apoyo de manera explícita. Normalmente, aplaudiría las condiciones de falla explícitas, pero esta no está documentada (por lo que puedo decir) y da como resultado una aplicación completamente rota, en lugar de una que simplemente opera con un rendimiento potencialmente degradado.
En la práctica, el único lugar en el que he visto esto es cuando interactúo con stdio. Un usuario puede redirigir stdin o stdout desde / hacia un archivo normal. Mientras que anteriormente stdin y stdout habrían sido una tubería, compatible con epoll sin problemas, luego se convierte en un archivo normal y epoll falla ruidosamente, rompiendo la aplicación.
poll
para completar?