Optimierung von Linux für die Verarbeitung von 1.2 Millionen JSON-Anfragen pro Sekunde

Es wurde eine detaillierte Anleitung zur Optimierung der Linux-Umgebung veröffentlicht, um maximale Leistung bei der Verarbeitung von HTTP-Anfragen zu erreichen. Die vorgeschlagenen Methoden ermöglichten es, die Leistung des JSON-Prozessors basierend auf der Libreactor-Bibliothek in der Amazon EC2-Umgebung (4 vCPU) von 224 API-Anfragen pro Sekunde mit Standardeinstellungen von Amazon Linux 2 mit Kernel 4.14 auf 1.2 Millionen Anfragen pro Sekunde zu steigern an zweiter Stelle nach der Optimierung (eine Steigerung von 436 %) und führte zudem zu einer Reduzierung der Verzögerungen bei der Bearbeitung von Anfragen um 79 %. Die vorgeschlagenen Methoden sind nicht spezifisch für Libreactor und funktionieren bei Verwendung anderer http-Server, einschließlich Nginx, Actix, Netty und Node.js (Libreactor wurde in Tests verwendet, weil die darauf basierende Lösung eine bessere Leistung zeigte).

Optimierung von Linux für die Verarbeitung von 1.2 Millionen JSON-Anfragen pro Sekunde

Grundlegende Optimierungen:

  • Optimierung des Libreactor-Codes. Als Basis diente die R18-Option aus dem Techempower-Kit, die durch das Entfernen von Code zur Begrenzung der Anzahl der verwendeten CPU-Kerne verbessert wurde (durch die Optimierung konnte die Arbeit um 25–27 % beschleunigt werden) und in GCC mit den „-O3“-Optionen zusammengestellt werden (eine Steigerung von 5–10 %) und „-march-native“ (5–10 %), wodurch Lese-/Schreibaufrufe durch recv/send (5–10 %) ersetzt und der Overhead bei der Verwendung von Pthreads reduziert wird (2–3 %). . Die Gesamtleistungssteigerung nach der Codeoptimierung betrug 55 %, und der Durchsatz stieg von 224 Anforderungen/s auf 347 Anforderungen/s.
  • Deaktivieren Sie den Schutz vor spekulativen Ausführungsschwachstellen. Durch die Verwendung der Parameter „nospectre_v1 nospectre_v2 pti=off mds=off tsx_async_abort=off“ beim Laden des Kernels konnte die Leistung um 28 % gesteigert und der Durchsatz von 347 req/s auf 446 req/s erhöht werden. Unabhängig davon betrug der Anstieg gegenüber dem Parameter „nospectre_v1“ (Schutz vor Spectre v1 + SWAPGS) 1–2 %, „nospectre_v2“ (Schutz vor Spectre v2) – 15–20 %, „pti=off“ (Spectre v3/Meltdown) – 6 %, „mds=off tsx_async_abort=off“ (MDS/Zombieload und TSX Asynchronous Abort) – 6 %. Die Einstellungen zum Schutz vor L1TF/Foreshadow- (l1tf=flush), iTLB-Multihit-, Speculative Store Bypass- und SRBDS-Angriffen wurden unverändert gelassen, was sich nicht auf die Leistung auswirkte, da sie sich nicht mit der getesteten Konfiguration überschnitten (z. B. spezifisch für KVM, verschachtelt). Virtualisierung und andere CPU-Modelle).
  • Deaktivieren von Überwachungs- und Systemaufrufblockierungsmechanismen mit dem Befehl „auditctl -a never,task“ und Angabe der Option „--security-opt seccomp=unconfined“ beim Starten des Docker-Containers. Die Gesamtleistungssteigerung betrug 11 %, und der Durchsatz stieg von 446 Anforderungen/s auf 495 Anforderungen/s.
  • Deaktivieren von iptables/netfilter durch Entladen der zugehörigen Kernelmodule. Die Idee, die Firewall zu deaktivieren, die in einer bestimmten Serverlösung nicht verwendet wurde, wurde durch Profiling-Ergebnisse angeregt, nach denen die Funktion nf_hook_slow 18 % der Ausführungszeit benötigte. Es wird darauf hingewiesen, dass nftables effizienter arbeitet als iptables, Amazon Linux verwendet jedoch weiterhin iptables. Nach der Deaktivierung von iptables betrug die Leistungssteigerung 22 % und der Durchsatz stieg von 495 Anforderungen/s auf 603 Anforderungen/s.
  • Reduzierte Migration von Handlern zwischen verschiedenen CPU-Kernen, um die Effizienz der Prozessor-Cache-Nutzung zu verbessern. Die Optimierung erfolgte sowohl auf der Ebene der Bindung von Libreactor-Prozessen an CPU-Kerne (CPU-Pinning) als auch durch das Pinning von Kernel-Netzwerkhandlern (Receive Side Scaling). Beispielsweise wurde irqbalance deaktiviert und die Warteschlangenaffinität zur CPU wurde explizit in /proc/irq/$IRQ/smp_affinity_list festgelegt. Um denselben CPU-Kern für die Verarbeitung des Libreactor-Prozesses und der Netzwerkwarteschlange eingehender Pakete zu verwenden, wird ein benutzerdefinierter BPF-Handler verwendet, der durch Setzen des Flags SO_ATTACH_REUSEPORT_CBPF beim Erstellen des Sockets verbunden wird. Um Warteschlangen ausgehender Pakete an die CPU zu binden, wurden die Einstellungen /sys/class/net/eth0/queues/tx- geändert /xps_cpus. Die Gesamtleistungssteigerung betrug 38 %, und der Durchsatz stieg von 603 Anforderungen/s auf 834 Anforderungen/s.
  • Optimierung des Interrupt-Handlings und der Verwendung von Polling. Durch die Aktivierung des Adaptive-Rx-Modus im ENA-Treiber und die Manipulation von sysctl net.core.busy_read wurde die Leistung um 28 % gesteigert (der Durchsatz stieg von 834 req/s auf 1.06 Mio. req/s und die Latenz verringerte sich von 361 μs auf 292 μs).
  • Deaktivieren von Systemdiensten, die zu unnötigen Blockaden im Netzwerkstapel führen. Das Deaktivieren von dhclient und das manuelle Festlegen der IP-Adresse führten zu einer Leistungssteigerung von 6 % und der Durchsatz stieg von 1.06 Mio. Anforderungen/s auf 1.12 Mio. Anforderungen/s. Der Grund dafür, dass dhclient die Leistung beeinträchtigt, liegt in der Verkehrsanalyse mithilfe eines Raw-Sockets.
  • Kampf gegen Spin Lock. Das Umschalten des Netzwerkstapels in den „noqueue“-Modus über sysctl „net.core.default_qdisc=noqueue“ und „tc qdisc replacement dev eth0 root mq“ führte zu einer Leistungssteigerung von 2 % und der Durchsatz stieg von 1.12 Mio. req/s auf 1.15 Mio Anforderung/s.
  • Letzte kleinere Optimierungen, wie das Deaktivieren von GRO (Generic Receive Offload) mit dem Befehl „ethtool -K eth0 gro off“ und das Ersetzen des kubischen Überlastungskontrollalgorithmus durch reno unter Verwendung von sysctl „net.ipv4.tcp_congestion_control=reno“. Die Gesamtproduktivitätssteigerung betrug 4 %. Der Durchsatz stieg von 1.15 Mio. Anforderungen/s auf 1.2 Mio. Anforderungen/s.

Neben den Optimierungen, die funktioniert haben, werden in dem Artikel auch Methoden besprochen, die nicht zu der erwarteten Leistungssteigerung führten. Folgendes erwies sich beispielsweise als wirkungslos:

  • Die separate Ausführung von libreactor unterschied sich in der Leistung nicht von der Ausführung in einem Container. Das Ersetzen von writev durch send, das Erhöhen von maxevents in epoll_wait und das Experimentieren mit GCC-Versionen und Flags hatten keine Auswirkungen (die Auswirkung war nur für die Flags „-O3“ und „-march-native“ spürbar).
  • Das Upgrade des Linux-Kernels auf die Versionen 4.19 und 5.4 unter Verwendung der SCHED_FIFO- und SCHED_RR-Scheduler sowie die Manipulation von sysctl kernel.sched_min_granularity_ns, kernel.sched_wakeup_granularity_ns, transparent_hugepages=never, skew_tick=1 und clocksource=tsc hatten keine Auswirkungen auf die Leistung.
  • Im ENA-Treiber hatte das Aktivieren der Offload-Modi (Segmentierung, Scatter-Gather, Rx/Tx-Prüfsumme), das Erstellen mit dem „-O3“-Flag und die Verwendung der Parameter ena.rx_queue_size und ena.force_large_llq_header keine Auswirkung.
  • Änderungen im Netzwerk-Stack führten nicht zu einer Leistungsverbesserung:
    • IPv6 deaktivieren: ipv6.disable=1
    • VLAN deaktivieren: modprobe -rv 8021q
    • Deaktivieren Sie die Überprüfung der Paketquelle
      • net.ipv4.conf.all.rp_filter=0
      • net.ipv4.conf.eth0.rp_filter=0
      • net.ipv4.conf.all.accept_local=1 (negativer Effekt)
    • 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_PRIORITÄT
    • TCP_NODELAY

    Source: opennet.ru

Kommentar hinzufügen