linux - open - ¿Cómo manejar con mayor eficiencia un gran número de descriptores de archivos?
ulimit (4)
Yo uso epoll () extensamente, y funciona bien. Tengo rutinariamente miles de enchufes activos y pruebo con hasta 131,072 enchufes. Y epoll () siempre puede manejarlo.
Utilizo múltiples hilos, cada uno de los cuales sondea en un subconjunto de sockets. Esto complica el código, pero aprovecha al máximo las CPU multinúcleo.
Parece haber varias opciones disponibles para programas que manejan grandes cantidades de conexiones de socket (como servicios web, sistemas p2p, etc.).
- Genere un hilo separado para manejar E / S para cada socket.
- Utilice la llamada al sistema de selección para multiplexar la E / S en un solo hilo.
- Use la llamada del sistema de sondeo para multiplexar la E / S (reemplazando la selección).
- Utilice las llamadas al sistema epoll para evitar tener que enviar repetidamente sockets fd a través de los límites del usuario / sistema.
- Genera una serie de subprocesos de E / S que multiplexan un conjunto relativamente pequeño de la cantidad total de conexiones que usan la API de sondeo.
- Según el n. ° 5, excepto que se usa la API de epoll para crear un objeto epoll separado para cada subproceso de E / S independiente.
En una CPU multinúcleo, esperaría que el n. ° 5 o el n. ° 6 tuvieran el mejor rendimiento, pero no tengo datos duros que respalden esto. Al buscar en la web apareció esta página que describe las experiencias de los enfoques de prueba del autor n. ° 2, n. ° 3 y n. ° 4 anteriores. Lamentablemente, esta página web parece tener alrededor de 7 años de antigüedad y no se encuentran actualizaciones recientes obvias.
Entonces mi pregunta es ¿cuáles de estos enfoques han encontrado que las personas son más eficientes y / o hay otro enfoque que funciona mejor que cualquiera de los enumerados anteriormente? Se apreciarán las referencias a gráficos de la vida real, libros blancos y / o grabaciones disponibles en la web.
Hablando con mi experiencia con la ejecución de grandes servidores IRC, solíamos usar select () y poll () (porque epoll () / kqueue () no estaban disponibles). En alrededor de 700 clientes simultáneos, el servidor usaría el 100% de una CPU (el servidor irc no tenía multiproceso). Sin embargo, curiosamente el servidor aún funcionaría bien. Con alrededor de 4.000 clientes, el servidor comenzaría a retrasarse.
La razón de esto fue que alrededor de 700.000 clientes, cuando volvíamos a seleccionar (), habría un cliente disponible para procesar. Los bucles for () bucles para descubrir qué cliente era consumirían la mayor parte de la CPU. A medida que obtuvimos más clientes, comenzamos a obtener más y más clientes que necesitaban procesamiento en cada llamada para seleccionar (), de modo que nos volviéramos más eficientes.
Pasando a epoll () / kqueue (), las máquinas de especificaciones similares tratarían trivialmente a 10,000 clientes, con algunas (máquinas admitidamente más poderosas, pero aún máquinas que se considerarían pequeñas para los estándares actuales), han tenido 30,000 clientes sin romper una sudor.
Los experimentos que he visto con SIGIO parecen sugerir que funcionan bien para aplicaciones donde la latencia es extremadamente importante, donde solo hay unos pocos clientes activos que realizan muy poco trabajo individual.
Recomiendo usar epoll () / kqueue () sobre select () / poll () en casi cualquier situación. No he experimentado con dividir clientes entre hilos. Para ser sincero, nunca encontré un servicio que necesitara más trabajo de optimización en el procesamiento del cliente front-end para justificar la experimentación con hilos.
He pasado los 2 últimos años trabajando en ese tema específico (para el servidor web G-WAN, que viene con MUCHOS puntos de referencia y gráficos que exponen todo esto).
El modelo que mejor funciona en Linux es el epoll con una cola de eventos (y, para el procesamiento pesado, varios subprocesos de trabajo).
Si tiene poco procesamiento (baja latencia de procesamiento) entonces usar un hilo será más rápido usando varios hilos.
La razón de esto es que epoll no escala en CPU multi-Core (el uso de varias colas epoll concurrentes para E / S de conexión en la misma aplicación en modo usuario ralentizará su servidor).
No miré seriamente el código de epoll en el kernel (solo me enfoqué en el modo de usuario hasta el momento), pero creo que la implementación de epoll en el núcleo está paralizada por bloqueos.
Esta es la razón por la cual usar varios hilos golpea rápidamente la pared.
No hace falta decir que un estado de cosas tan pobre no debería durar si Linux quiere mantener su posición como uno de los kernels con mejor rendimiento.
Según mi experiencia, tendrás la mejor perfomance con el # 6.
También te recomiendo que estudies libevent para tratar de abstraer algunos de estos detalles. Por lo menos, podrás ver algunos de sus puntos de referencia .
Además, ¿de cuántos enchufes está hablando? Su enfoque probablemente no importe demasiado hasta que empiece a obtener al menos unos cientos de zócalos.