Objavljeno je podrobno vodilo za optimizacijo okolja Linux za dosego maksimalne zmogljivosti obdelave HTTP zahtevkov. Predlagane metode so omogočile povečanje zmogljivosti obdelovalnika JSON na osnovi knjižnice libreactor v okolju Amazon EC2 (4 vCPU) s 224 tisoč API zahtevki na sekundo pri privzetih nastavitvah Amazon Linux 2 z jedrom 4.14 do 1,2 milijona zahtevkov na sekundo po optimizaciji (povečanje 436 %), prav tako pa so pripeljale do zmanjšanja zakasnitev pri obdelavi zahtevkov za 79 %. Predlagane metode niso specifične za libreactor in delujejo tudi pri uporabi drugih http strežnikov, vključno z nginx, Actix, Netty in Node.js (libreactor je bil uporabljen pri testih, saj je rešitev na njegovi osnovi pokazala najboljšo zmogljivost).

Glavne optimizacije:
- Optimizacija kode libreactor. Kot osnova je bila uporabljena različica R18 iz nabora Techempower, ki je bila izboljšana z odstranitvijo kode za omejevanje števila uporabljenih jeder CPU (optimizacija je omogočila pospešitev dela za 25-27 %), gradnjo v GCC z možnostmi „-O3“ (povečanje 5-10 %) in „-march-native“ (5-10 %), zamenjavo klicev read/write z recv/send (5-10 %) in znižanjem stroškov pri uporabi pthreads (2-3 %). Skupno povečanje zmogljivosti po optimizaciji kode je znašalo 55 %, prepustnost pa se je povečala s 224k req/s na 347k req/s.
- Onemogočitev zaščite pred ranljivostmi, ki jih povzroča spekulativno izvajanje ukazov. Uporaba parametrov pri nalaganju jedra „nospectre_v1 nospectre_v2 pti=off mds=off tsx_async_abort=off“ je omogočila povečanje zmogljivosti za 28 %, prepustnost pa se je povečala s 347k req/s na 446k req/s. Posamezno povečanje zaradi parametra „nospectre_v1“ (zaščita pred Spectre v1 + SWAPGS) je bilo 1-2 %, „nospectre_v2“ (zaščita pred Spectre v2) — 15-20 %, „pti=off“ (Spectre v3 / Meltdown) — 6 %, „mds=off tsx_async_abort=off“ (MDS / Zombieload in TSX Asynchronous Abort) — 6 %. Nastavitve za zaščito pred napadi L1TF / Foreshadow (l1tf=flush), iTLB multihit, Speculative Store Bypass in SRBDS so ostale nespremenjene, saj niso vplivale na zmogljivost, ker se niso prepletale s testirano konfiguracijo (na primer specifične za KVM, vgrajeno virtualizacijo in druge modele CPU).
- Onemogočitev mehanizmov za revizijo in blokiranje sistemskih klicev s pomočjo ukaza „auditctl -a never,task“ in navedbo možnosti „—security-opt seccomp=unconfined“ pri zagonu docker kontejnerja. Skupno povečanje zmogljivosti je bilo 11 %, prepustnost pa se je povečala s 446k req/s na 495k req/s.
- Izključitev iptables/netfilterja z nalaganjem povezanih modulov jedra. Idejo o izklopu požarnega zidu, ki ni bil uporabljen v specifični strežniški rešitvi, je spodbudila profiliranje, iz katerega je razvidno, da je za izvajanje funkcije nf_hook_slow porabljalo 18% časa. Opozarja se, da nftables deluje učinkoviteje kot iptables, vendar se v Amazon Linux še vedno uporablja iptables. Po izključitvi iptables je pridobitev zmogljivosti znašala 22%, propustnost pa se je povečala s 495k req/s na 603k req/s.
- Zmanjšanje migracij obdelovalcev med različnimi jedri CPU za povečanje učinkovitosti uporabe procesorskega predpomnilnika. Optimizacija je bila narejena na ravni vezave procesov libreactor na jedra CPU (CPU Pinning), kot tudi preko zaklenitve omrežnih obdelovalcev jedra (Receive Side Scaling). Na primer, izvedeno je bilo izklopitev irqbalance in izrecna nastavitev vezav vrstic na CPU v /proc/irq/$IRQ/smp_affinity_list. Za uporabo istega jedra CPU za obdelavo procesa libreactor in omrežne vrste prihajajočih paketov je bil uporabljen lastni BPF-obdelovalec, povezan preko nastavitve zastavice SO_ATTACH_REUSEPORT_CBPF pri ustvarjanju socket. Za vezavo na CPU vrstic odhodnih paketov so bile spremenjene nastavitve /sys/class/net/eth0/queues/tx-<n>/xps_cpus. Skupna pridobitev zmogljivosti je znašala 38%, propustnost pa se je povečala s 603k req/s na 834k req/s.
- Optimizacija obdelave prekinitev in uporaba polinga (polling). Vklop načina adaptive-rx v gonilniku ENA in manipulacije s sysctl net.core.busy_read so omogočili povečanje zmogljivosti za 28% (propustnost se je povečala s 834k req/s na 1.06M req/s, zakasnitve pa so se znižale s 361μs na 292μs).
- Izključitev sistemskih storitev, ki povzročajo nepotrebne zaklepe v omrežnem skladu. Izključitev dhclient in nastavitev IP-naslove ročno je pripeljala do povečanja zmogljivosti za 6%, propustnost pa se je povečala s 1.06M req/s na 1.12M req/s. Razlog za vpliv dhclient na zmogljivost je analiza prometa z uporabo raw-socket.
- Boj proti Spin Lock. Prehod omrežnega sklada v način 'noqueue' preko sysctl 'net.core.default_qdisc=noqueue' in 'tc qdisc replace dev eth0 root mq' je privedel do povečanja zmogljivosti za 2%, propustnost pa se je povečala s 1.12M req/s na 1.15M req/s.
- Končne manjše optimizacije, kot je izklop GRO (Generic Receive Offload) z ukazom „ethtool -K eth0 gro off“ in zamenjava algoritma nadzora zagušenja cubic z reno z uporabo sysctl „net.ipv4.tcp_congestion_control=reno“. Skupno povečanje zmogljivosti je znašalo 4 %. Prehodnost je narasla s 1.15M req/s na 1.2M req/s.
Poleg izvedenih optimizacij članek obravnava tudi metode, ki niso privedle do pričakovanega povečanja zmogljivosti. Na primer, se je izkazalo, da so neučinkovite:
- Ločeno zagon libreactor ni bil bolj zmogljiv kot zagon v vsebniko. Zamenjava writev z send, povečanje maxevents v epoll_wait in eksperimenti s različicami in zastavami GCC (učinek je bil opazen le pri zastavah „-O3“ in „-march-native“) niso vplivali na zmogljivost.
- Posodobitev jedra Linux na različice 4.19 in 5.4, uporaba časovnikov SCHED_FIFO in SCHED_RR, manipulacije s sysctl kernel.sched_min_granularity_ns, kernel.sched_wakeup_granularity_ns, transparent_hugepages=never, skew_tick=1 in clocksource=tsc pa prav tako niso vplivale na zmogljivost.
- V gonilniku ENA vključitev načinov Offload (segmentation, scatter-gather, rx/tx checksum), gradnja s zastavo „-O3“ in uporaba parametrov ena.rx_queue_size in ena.force_large_llq_header niso vplivali na zmogljivost.
- Spremembe v omrežnem stacku niso privedle do povečanja zmogljivosti:
- Izklop IPv6: ipv6.disable=1
- Izklop VLAN: modprobe -rv 8021q
- Izklop preverjanja vira paketa
- net.ipv4.conf.all.rp_filter=0
- net.ipv4.conf.eth0.rp_filter=0
- net.ipv4.conf.all.accept_local=1 (negativen učinek)
- 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_PRIORITY
- TCP_NODELAY
Izvor: opennet.ru
