que - socket servidor c++
C++ Socket Server-No se puede saturar la CPU (6)
¿Cuántas instancias de io_service tienes? Boost asio tiene un example que crea un io_service por CPU y los usa a la manera de RoundRobin.
Aún puede crear cuatro subprocesos y asignar uno por CPU, pero cada subproceso puede sondear en su propio io_service.
He desarrollado un mini servidor HTTP en C ++, usando boost :: asio, y ahora lo estoy probando con varios clientes y no he podido acercarme a la saturación de la CPU. Estoy probando en una instancia de Amazon EC2, y obtengo aproximadamente el 50% de uso de una CPU, el 20% de otra, y las dos restantes están inactivas (según htop).
Detalles:
- El servidor enciende un hilo por núcleo
- Las solicitudes se reciben, se analizan, se procesan y las respuestas se escriben
- Las solicitudes son para datos, que se leen de memoria (solo lectura para esta prueba)
- Estoy ''cargando'' el servidor usando dos máquinas, cada una ejecutando una aplicación java, ejecutando 25 subprocesos, enviando solicitudes
- Veo alrededor de 230 solicitudes / seg. (Esto es solicitudes de aplicaciones , que están compuestas por muchas solicitudes HTTP)
Entonces, ¿qué debo mirar para mejorar este resultado? Dado que la CPU está en su mayoría inactiva, me gustaría aprovechar esa capacidad adicional para obtener un rendimiento más alto, digamos 800 solicitudes por segundo o lo que sea.
Ideas que he tenido:
- Las solicitudes son muy pequeñas y, a menudo, se cumplen en unos pocos ms. Puedo modificar el cliente para enviar / componer solicitudes más grandes (quizás utilizando lotes)
- Podría modificar el servidor HTTP para usar el patrón de diseño Select, ¿es apropiado aquí?
- Podría hacer un poco de perfil para tratar de entender lo que son / son los cuellos de botella
230 solicitudes / seg parece ser muy bajo para tales solicitudes asíncronas simples. Como tal, el uso de múltiples subprocesos es probablemente una optimización prematura; consiga que funcione correctamente y sintonice en un único subproceso, y vea si todavía los necesita. El solo hecho de deshacerse del bloqueo innecesario puede acelerar el proceso.
Este artículo tiene algunos detalles y una discusión sobre las estrategias de E / S para el rendimiento del estilo de servidor web alrededor de 2003. ¿Alguien tiene algo más reciente?
ASIO está bien para tareas pequeñas a medianas, pero no es muy bueno para aprovechar el poder del sistema subyacente. Tampoco lo son las llamadas de socket sin formato, ni siquiera IOCP en Windows, pero si tiene experiencia, siempre será mejor que ASIO. De cualquier manera, hay muchos gastos generales con todos esos métodos, solo más con ASIO.
Por lo que vale la pena. el uso de llamadas de socket sin formato en mi HTTP personalizado puede atender solicitudes dinámicas de 800K por segundo con un I7 de 4 núcleos. Está sirviendo desde la RAM, que es donde debe estar para ese nivel de rendimiento. En este nivel de rendimiento, el controlador de red y el sistema operativo consumen aproximadamente el 40% de la CPU. Usando ASIO puedo obtener alrededor de 50 a 100K solicitudes por segundo, su rendimiento es bastante variable y está principalmente vinculado a mi aplicación. La publicación de @cmeerw explica principalmente por qué.
Una forma de mejorar el rendimiento es mediante la implementación de un proxy UDP. Al interceptar solicitudes HTTP y luego enviarlas a través de UDP a su servidor UDP-HTTP backend, puede evitar una gran cantidad de sobrecarga de TCP en las pilas del sistema operativo. También puede tener extremos frontales que se canalizan a través de UDP, lo que no debería ser demasiado difícil de hacer usted mismo. Una ventaja de un proxy HTTP-UDP es que le permite usar cualquier buen frontend sin modificaciones, y puede intercambiarlos a voluntad sin ningún impacto. Solo necesitas un par de servidores más para implementarlo. Esta modificación en mi ejemplo redujo el uso de la CPU del sistema operativo al 10%, lo que aumentó mis solicitudes por segundo a poco más de un millón en ese único backend. Y FWIW Siempre debe tener una configuración de frontend-backend para cualquier sitio de rendimiento porque los frontends pueden almacenar datos en caché sin ralentizar el backend de las solicitudes dinámicas más importantes.
El futuro parece ser escribir su propio controlador que implementa su propia pila de red para que pueda acercarse lo más posible a las solicitudes e implementar su propio protocolo allí. Lo que probablemente no es lo que la mayoría de los programadores quieren escuchar, ya que es más complicado. En mi caso, podría usar un 40% más de CPU y pasar a más de 1 millón de solicitudes dinámicas por segundo. El método de proxy UDP puede acercarlo a un rendimiento óptimo sin necesidad de hacer esto, sin embargo, necesitará más servidores, aunque si realiza esta cantidad de solicitudes por segundo, generalmente necesitará varias tarjetas de red y múltiples interfaces para manejar el ancho de banda, por lo que un par de proxies UDP ligeros allí no es tan importante.
Espero que algo de esto te sea de utilidad.
Como está utilizando EC2, todas las apuestas están desactivadas.
Pruébelo usando hardware real, y luego podrá ver lo que está sucediendo. Tratar de hacer pruebas de rendimiento en máquinas virtuales es básicamente imposible.
Todavía no he descubierto para qué sirve EC2. Si alguien lo descubre, por favor, hágamelo saber.
De sus comentarios sobre la utilización de la red,
No pareces tener mucho movimiento de red.
3 + 2.5 MiB/sec
es alrededor del parque de pelota de 50Mbps
(comparado con su puerto de 1Gbps).
Yo diría que está teniendo uno de los siguientes dos problemas,
- Carga de trabajo insuficiente (baja tasa de solicitud de sus clientes)
- Bloqueo en el servidor (generación de respuesta interferida)
Mirando las notas de cmeerw
y sus cifras de utilización de CPU
(ralentí al 50% + 20% + 0% + 0%
)
Parece más probable que sea una limitación en la implementación de su servidor.
Segunda respuesta de cmeerw
(+1).
boost :: asio no es tan amigable para los hilos como esperaría, hay un gran bloqueo alrededor del código de epoll en boost / asio / detail / epoll_reactor.hpp lo que significa que solo un hilo puede llamar al sistema de epoll del kernel a la vez . Y para solicitudes muy pequeñas, esto marca la diferencia (lo que significa que solo verá un rendimiento de un solo hilo).
Tenga en cuenta que esta es una limitación de cómo boost :: asio utiliza las instalaciones del kernel de Linux, no necesariamente el propio kernel de Linux. Epoll syscall admite varios subprocesos cuando se usan eventos desencadenados por el borde, pero hacerlo bien (sin un bloqueo excesivo) puede ser bastante complicado.
Por cierto, he estado haciendo algo de trabajo en esta área (combinando un bucle de eventos de epoll desencadenado por el borde completamente multihilo con hilos / fibras programados por el usuario) e hice un código disponible bajo el proyecto nginetd .