Аптымізацыя Linux для апрацоўкі 1.2 млн JSON-запытаў у секунду

Апублікавана дэталёвае кіраўніцтва па цюнінгу асяроддзя Linux для дасягнення максімальнай прадукцыйнасці апрацоўкі HTTP-запытаў. Прапанаваныя метады дазволілі падняць прадукцыйнасць апрацоўшчыка JSON на аснове бібліятэкі libreactor у асяроддзі Amazon EC2 (4 vCPU) c 224 тысяч запытаў API у секунду пры штатных наладах Amazon Linux 2 з ядром 4.14 да 1.2 млн запытаў у секунду пасля правядзення аптымізацыі (прырост 436 а таксама прывялі да скарачэння затрымак пры апрацоўцы запытаў на 79%. Прапанаваныя метады не спецыфічныя для libreactor і працуюць пры выкарыстанні іншых http-сервераў, у тым ліку nginx, Actix, Netty і Node.js (libreactor выкарыстоўваўся ў тэстах, бо рашэнне на яго аснове паказала лепшую прадукцыйнасць).

Аптымізацыя Linux для апрацоўкі 1.2 млн JSON-запытаў у секунду

Асноўныя аптымізацыі:

  • Аптымізацыя кода libreactor. У якасці асновы быў выкарыстаны варыянт R18 з набору Techempower, які быў палепшаны шляхам выдалення кода для абмежавання колькасці ядраў CPU (аптымізацыя дазволіла паскорыць працу на 25-27%), зборкі ў GCC з опцыямі «-O3» (прырост 5-10%. ) і "-march-native" (5-10%), замены выклікаў read/write на recv/send (5-10%) і зніжэння накладных выдаткаў пры выкарыстанні pthreads (2-3%). Агульны прырост прадукцыйнасці пасля аптымізацыі кода склаў 55%, а прапускная здольнасць узрасла з 224k req/s да 347k req/s.
  • Адключэнне абароны ад уразлівасцяў, выкліканых спекулятыўным выкананнем інструкцый. Выкарыстанне параметраў пры загрузцы ядра дазволіла падняць прадукцыйнасць на 1%, а прапускная здольнасць узрасла з 2k req/s да 28k req/s. У асобнасці прырост ад параметру "nospectre_v347" (абарона ад Spectre v446 + SWAPGS) склаў 1-1%, "nospectre_v1" (абарона ад Spectre v2) - 2-2%, "pti = off" (Spectre v15 / Meltdown) - 20 %, "mds = off tsx_async_abort = off" (MDS / Zombieload і TSX Asynchronous Abort) – 3%. Пакінутыя без змены налады для абароны ад нападаў L6TF/Foreshadow (l6tf=flush), iTLB multihit, Speculative Store Bypass і SRBDS, якія не ўплывалі на прадукцыйнасць, бо не перасякаліся з тэстоўванай канфігурацыяй (напрыклад, спецыфічныя для KVM, укладзенай віртуалізацыі і іншых мадэляў CPU).
  • Адключэнне механізмаў аўдыту і блакаванні сістэмных выклікаў пры дапамозе каманды "auditctl -a never, task" і ўказанні опцыі "-security-opt seccomp=unconfined" пры запуску кантэйнера docker. Агульны прырост прадукцыйнасці склаў 11%, а прапускная здольнасць узрасла з 446k req/s да 495k req/s.
  • Адключэнне iptables/netfilter праз выгрузку злучаных з імі модуляў ядра. Да ідэі адключыць міжсеткавы экран, які не выкарыстоўваўся ў спецыфічным серверным рашэнні, падштурхнулі вынікі прафілявання, мяркуючы па якіх на выкананне функцыі nf_hook_slow сыходзіла 18% часу. Адзначаецца, што nftables працуе больш эфектыўна, чым iptables, але ў Amazon Linux працягвае выкарыстоўвацца iptables. Пасля адключэння iptables прырост прадукцыйнасці склаў 22%, а прапускная здольнасць узрасла з 495k req/s да 603k req/s.
  • Зніжэнне міграцыі апрацоўшчыкаў паміж рознымі ядрамі CPU для павышэння эфектыўнасці выкарыстання працэсарнага кэша. Аптымізацыя была праведзена як на ўзроўні прывязкі працэсаў libreactor да ядраў CPU (CPU Pinning), так і праз замацаванне сеткавых апрацоўшчыкаў ядра (Receive Side Scaling). Напрыклад, выканана адключэнне irqbalance і відавочнае выстаўленне прывязак чэргаў да CPU у /proc/irq/$IRQ/smp_affinity_list. Для выкарыстання аднаго і таго ж ядра CPU для апрацоўкі працэсу libreactor і сеткавай чаргі ўваходных пакетаў задзейнічаны ўласны BPF-апрацоўшчык, падлучаны праз усталёўку сцяга SO_ATTACH_REUSEPORT_CBPF пры стварэнні сокета. Для прывязкі да CPU чэргаў выходных пакетаў зменены налады /sys/class/net/eth0/queues/tx- /xps_cpus. Агульны прырост прадукцыйнасці склаў 38%, а прапускная здольнасць узрасла з 603k req/s да 834k req/s.
  • Аптымізацыя апрацоўкі перапыненняў і выкарыстанне полінга (polling). Уключэнне рэжыму adaptive-rx у драйверы ENA і маніпуляцыі з sysctl net.core.busy_read дазволілі падняць прадукцыйнасць на 28% (прапускная здольнасць узрасла з 834k req/s да 1.06M req/s, а затрымкі знізіліся з 361µs).
  • Адключэнне сістэмных сэрвісаў, якія прыводзяць да лішніх блакіровак у сеткавым стэку. Адключэнне dhclient і ўсталёўка IP-адрасы ўручную прывяла да падвышэння прадукцыйнасці на 6%, а прапускная здольнасць узрасла з 1.06M req/s да 1.12M req/s. Чыннік уплыву dhclient на прадукцыйнасць у аналізе трафіку з выкарыстаннем raw-сокета.
  • Барацьба са Spin Lock. Перавод сеткавага стэка ў рэжым "noqueue" праз sysctl "net.core.default_qdisc=noqueue" і "tc qdisc replace dev eth0 root mq" прывёў да прыросту прадукцыйнасці на 2%, а прапускная здольнасць узрасла з 1.12M req/s да 1.15. req/s.
  • Фінальныя дробныя аптымізацыі, такія як адключэнне GRO (Generic Receive Offload) камандай "ethtool -K eth0 gro off" і замена алгарытму кантролю за перагрузкай cubic на reno пры дапамозе sysctl "net.ipv4.tcp_congestion_control=reno". Агульны прырост прадукцыйнасці склаў 4 працэнты. Прапускная здольнасць узрасла з 1.15M req/s да 1.2M req/s.

Акрамя аптымізацый, якія спрацавалі, у артыкуле таксама разглядаюцца метады, якія не прывялі да чаканага росту прадукцыйнасці. Напрыклад, неэфектыўнымі аказаліся:

  • Асобны запуск libreactor не адрозніваўся ў прадукцыйнасці ад запуску ў кантэйнеры. Не аказалі ўплыву замена writev на send, павелічэнне maxevents у epoll_wait, эксперыменты з версіямі і сцягамі GCC (эфект быў замецены толькі для сцягоў "-O3" і "-march-native").
  • Не паўплывала на прадукцыйнасць абнаўленне ядра Linux да версій 4.19 і 5.4, выкарыстанне планавальнікаў SCHED_FIFO і SCHED_RR, маніпуляцыі з sysctl kernel.sched_min_granularity_ns, kernel.sched_wakeup_granularity_ns, transparent_hu
  • У драйверы ENA не паўплывала ўключэнне рэжымаў Offload (segmentation, scatter-gather, rx/tx checksum), зборка са сцягам "-O3" і ўжыванне параметраў ena.rx_queue_size і ena.force_large_llq_header.
  • Не прывялі да павышэння прадукцыйнасці змены ў сеткавым стэку:
    • Адключэнне IPv6: ipv6.disable=1
    • Адключэнне VLAN: modprobe -rv 8021q
    • Адключэнне праверкі крыніцы пакета
      • net.ipv4.conf.all.rp_filter=0
      • net.ipv4.conf.eth0.rp_filter=0
      • net.ipv4.conf.all.accept_local=1 (негатыўны эфект)
    • 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

    Крыніца: opennet.ru

Дадаць каментар