Optimización de Linux para gestionar 1.2 millones de solicitudes JSON por segundo

Se ha publicado una guía detallada sobre cómo ajustar el entorno Linux para lograr el máximo rendimiento en el procesamiento de solicitudes HTTP. Los métodos propuestos permitieron aumentar el rendimiento del procesador JSON basado en la biblioteca libreactor en el entorno Amazon EC2 (4 vCPU) de 224 mil solicitudes API por segundo con la configuración estándar de Amazon Linux 2 con kernel 4.14 a 1.2 millones de solicitudes por segundo. segundo después de la optimización (un aumento del 436%), y también condujo a una reducción de los retrasos en el procesamiento de solicitudes en un 79%. Los métodos propuestos no son específicos de libreactor y funcionan cuando se utilizan otros servidores http, incluidos nginx, Actix, Netty y Node.js (se utilizó libreactor en las pruebas porque la solución basada en él mostró un mejor rendimiento).

Optimización de Linux para gestionar 1.2 millones de solicitudes JSON por segundo

Optimizaciones básicas:

  • Optimización del código libreactor. Se utilizó como base la opción R18 del kit Techempower, que se mejoró eliminando código para limitar la cantidad de núcleos de CPU utilizados (la optimización permitió acelerar el trabajo en un 25-27%), ensamblando en GCC con las opciones "-O3" (un aumento del 5-10%) y "-march-native" (5-10%), reemplazando las llamadas de lectura/escritura con recv/send (5-10%) y reduciendo la sobrecarga al usar pthreads (2-3%) . El aumento general del rendimiento después de la optimización del código fue del 55 %, y el rendimiento aumentó de 224 347 solicitudes/s a XNUMX XNUMX solicitudes/s.
  • Deshabilite la protección contra vulnerabilidades de ejecución especulativa. El uso de los parámetros “nospectre_v1 nospectre_v2 pti=off mds=off tsx_async_abort=off” al cargar el kernel permitió aumentar el rendimiento en un 28%, y el rendimiento aumentó de 347k solicitudes/s a 446k solicitudes/s. Por separado, el aumento del parámetro “nospectre_v1” (protección contra Spectre v1 + SWAPGS) fue del 1-2%, “nospectre_v2” (protección contra Spectre v2) - 15-20%, "pti=off" (Spectre v3/Meltdown) - 6 %, "mds=off tsx_async_abort=off" (MDS/Zombieload y TSX Asynchronous Abort) - 6%. Las configuraciones de protección contra ataques L1TF/Foreshadow (l1tf=flush), iTLB multihit, Speculative Store Bypass y SRBDS se dejaron sin cambios, lo que no afectó el rendimiento ya que no se cruzaban con la configuración probada (por ejemplo, específica de KVM, anidado). virtualización y otros modelos de CPU).
  • Deshabilitar los mecanismos de auditoría y bloqueo de llamadas del sistema usando el comando "auditctl -a never,task" y especificando la opción "--security-opt seccomp=unconfined" al iniciar el contenedor acoplable. El aumento general del rendimiento fue del 11 % y el rendimiento aumentó de 446 495 solicitudes/s a XNUMX XNUMX solicitudes/s.
  • Deshabilitar iptables/netfilter descargando los módulos del kernel asociados. La idea de desactivar el cortafuegos, que no se utilizaba en una solución de servidor específica, surgió de los resultados del perfil, a juzgar por los cuales la función nf_hook_slow tardó el 18% del tiempo en ejecutarse. Se observa que nftables funciona de manera más eficiente que iptables, pero Amazon Linux continúa usando iptables. Después de deshabilitar iptables, el aumento de rendimiento fue del 22 %, y el rendimiento aumentó de 495 603 solicitudes/s a XNUMX XNUMX solicitudes/s.
  • Migración reducida de controladores entre diferentes núcleos de CPU para mejorar la eficiencia del uso de la caché del procesador. La optimización se llevó a cabo tanto a nivel de vinculación de procesos de libreactor a núcleos de CPU (CPU Pinning) como mediante la vinculación de controladores de red del kernel (Receive Side Scaling). Por ejemplo, irqbalance se deshabilitó y la afinidad de la cola con la CPU se configuró explícitamente en /proc/irq/$IRQ/smp_affinity_list. Para usar el mismo núcleo de CPU para procesar el proceso libreactor y la cola de red de paquetes entrantes, se usa un controlador BPF personalizado, conectado configurando el indicador SO_ATTACH_REUSEPORT_CBPF al crear el socket. Para vincular colas de paquetes salientes a la CPU, se ha cambiado la configuración /sys/class/net/eth0/queues/tx-/xps_cpus. El aumento general del rendimiento fue del 38 % y el rendimiento aumentó de 603 834 solicitudes/s a XNUMX XNUMX solicitudes/s.
  • Optimización del manejo de interrupciones y uso de polling. Habilitar el modo adaptive-rx en el controlador ENA y manipular sysctl net.core.busy_read aumentó el rendimiento en un 28 % (el rendimiento aumentó de 834 k solicitudes/s a 1.06 millones de solicitudes/s y la latencia disminuyó de 361 μs a 292 μs).
  • Deshabilitar los servicios del sistema que provocan bloqueos innecesarios en la pila de red. La desactivación de dhclient y la configuración manual de la dirección IP dieron como resultado un aumento del rendimiento del 6 % y el rendimiento aumentó de 1.06 millones de solicitudes/s a 1.12 millones de solicitudes/s. La razón por la que dhclient afecta el rendimiento es en el análisis de tráfico utilizando un socket sin formato.
  • Lucha contra el bloqueo de giro. Cambiar la pila de red al modo "noqueue" a través de sysctl "net.core.default_qdisc=noqueue" y "tc qdisc replace dev eth0 root mq" generó un aumento del rendimiento del 2 % y el rendimiento aumentó de 1.12 millones de solicitudes/s a 1.15 millones. req/s.
  • Optimizaciones menores finales, como deshabilitar GRO (descarga de recepción genérica) con el comando “ethtool -K eth0 gro off” y reemplazar el algoritmo de control de congestión cúbico con reno usando sysctl “net.ipv4.tcp_congestion_control=reno”. El aumento general de la productividad fue del 4%. El rendimiento aumentó de 1.15 millones de solicitudes/s a 1.2 millones de solicitudes/s.

Además de las optimizaciones que funcionaron, el artículo también analiza métodos que no condujeron al aumento de rendimiento esperado. Por ejemplo, lo siguiente resultó ineficaz:

  • Ejecutar libreactor por separado no difirió en rendimiento de ejecutarlo en un contenedor. Reemplazar writev con send, aumentar maxevents en epoll_wait y experimentar con versiones e indicadores de GCC no tuvo ningún efecto (el efecto se notó solo para los indicadores “-O3” y “-march-native”).
  • Actualizar el kernel de Linux a las versiones 4.19 y 5.4, usar los programadores SCHED_FIFO y SCHED_RR, manipular sysctl kernel.sched_min_granularity_ns, kernel.sched_wakeup_granularity_ns, transparent_hugepages=never, skew_tick=1 y clocksource=tsc no afectó el rendimiento.
  • En el controlador ENA, habilitar los modos de descarga (segmentación, recopilación de dispersión, suma de comprobación rx/tx), compilar con el indicador "-O3" y usar los parámetros ena.rx_queue_size y ena.force_large_llq_header no tuvo ningún efecto.
  • Los cambios en la pila de red no mejoraron el rendimiento:
    • Deshabilitar IPv6: ipv6.disable=1
    • Deshabilitar VLAN: modprobe -rv 8021q
    • Deshabilitar la verificación del origen del paquete
      • net.ipv4.conf.all.rp_filter=0
      • net.ipv4.conf.eth0.rp_filter=0
      • net.ipv4.conf.all.accept_local=1 (efecto negativo)
    • net.ipv4.tcp_sack=0
    • net.ipv4.tcp_dsack=0
    • net.ipv4.tcp_mem/tcp_wmem/tcp_rmem
    • net.core.netdev_budget
    • net.core.dev_weight
    • net.core.netdev_max_backlog
    • net.ipv4.tcp_slow_start_after_idle=0
    • net.ipv4.tcp_moderate_rcvbuf=0
    • net.ipv4.tcp_timestamps=0
    • net.ipv4.tcp_low_latency=1
    • SO_PRIORIDAD
    • TCP_NODELAY

    Fuente: opennet.ru

Añadir un comentario