Ladenie linuxového jadra pre AI servery: čo naozaj zaberá
Predvolené jadro Ubuntu na stacku HWE 6.8 alebo 6.11 je pre približne 80 % AI workloadov úplne postačujúce. Tento článok je o zvyšných 20 % — viacsoketový EPYC poháňajúci osem GPU, vLLM servovací stroj, ktorý pod záťažou naráža na strop vm.max_map_count, alebo inferenčný server, ktorému koncovú latenciu požierajú prechody C-stavov CPU.
Väčšina verejne dostupných rád na „ladenie Linuxu pre AI“ je recyklovaná databázová príručka z roku 2015 a nezanedbateľná časť z nich latenciu inferencie potichu zhoršuje. Nižšie nájdete menšiu, poctivú množinu zmien, ktoré na stroji triedy Kentino AI (4–8 GPU na Xeone alebo EPYC, Ubuntu 22.04 / 24.04, jadro 6.x) naozaj pomáhajú. Predpokladáme, že ovládač a CUDA stack sú nainštalované podľa L01 a L02.
Poctivá hierarchia prínosov
Skôr než siahnete na akýkoľvek sysctl, ukotvite si veľkosť výhry:
| Zmena | Typický prínos pri inferencii / tréningu |
|---|---|
| Umiestňovanie procesov s ohľadom na NUMA | 10–30 % na viacsoketových strojoch |
Governor CPU performance (namiesto powersave) |
5–15 % na servovacích strojoch, nižší TTFT |
| Vypnutie hlbokých C-stavov | Mikrosekundy dole z P99, +30–50 W v pokoji na soket |
THP nastavené na madvise
|
1–5 % na PyTorchi, menej zadrhnutí pri vysokom obrate pamäte |
vm.max_map_count, ulimit -n
|
Zabráni pádu servovania pod záťažou |
| Afinita IRQ na lokálny NUMA uzol | 5–15 % na sieťových cestách DataLoader / RDMA |
Dimenzovanie TCP bufferov (rmem_max, wmem_max) |
Má význam len pre úložisko / streaming >25 GbE |
| Všetko ostatné | 0–3 %, často len šum |
Ak si z tohto článku máte odniesť jedinú vec: NUMA a governor CPU sú dve veľké výhry. Všetko ostatné je zaokrúhľovacia chyba, pokiaľ ste si to nezmerali.
Governor CPU: najlacnejších 5–15 %
Ubuntu podľa ovládača predvolene používa powersave (intel_pstate) alebo ondemand (acpi_cpufreq). Oba naťahujú takty reaktívne, čo stojí 5–15 % na TTFT inferencie a na prípravných CPU operáciách okolo doprednej passy vLLM — tokenizácia, plánovanie, vzorkovanie logitov.
Na servovacom stroji nastavte performance a zabudnite na to:
sudo apt install -y cpufrequtils
echo 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtils
sudo systemctl restart cpufrequtils
# Verify
for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do cat $c; done | sort -u
Na EPYC Genoa / Turin s jadrom 6.5+ nahrádza ovládač amd-pstate ovládač acpi-cpufreq. Princíp je rovnaký, nastavte scaling_governor na performance. Novší režim amd-pstate-epp udrží výkon a zároveň umožní boostovanie jednotlivých jadier; EPP nechajte na performance, nie balance_performance.
Výhrada: na tréningovom stroji pri trvalom ~100 % využití GPU sú procesory aj tak zaťažené a governor zaváži menej. Veľký prínos je na inferenčných serveroch, ktoré medzi požiadavkami zaháľajú a musia sa okamžite rozbehnúť.
NUMA: rozdiel medzi rýchlym a priemerným EPYC strojom
Jednosoketové servery Xeon alebo EPYC nemajú čo riešiť — jeden NUMA uzol, každý prístup do pamäte je lokálny. Tých 10–30 % sa skrýva v dvojsoketových strojoch.
Predvolený plánovač pokojne umiestni vLLM workera na soket 0 a nechá ho stránkovať do pamäte alokovanej na sokete 1 — každé načítanie je skok cez UPI / Infinity Fabric medzi soketmi. Pri modeli so stovkami GB váh, ktoré sa prejdú raz na každý token, je to reálny problém.
Zistenie stavu:
numactl --hardware # node count, sizes, distance matrix
nvidia-smi topo -m # GPU↔CPU NUMA affinity
cat /sys/class/net/<iface>/device/numa_node # NIC NUMA affinity
Vzor, ktorý chcete — a ktorý NCCL deteguje automaticky — je GPU N pripnuté na NUMA uzol hostiaci jeho PCIe root complex, nie na druhý soket. Pri ručne spustenom inferenčnom procese pripnite na lokálny uzol aj procesor, aj pamäť:
# vLLM on a 2-socket EPYC, GPUs 0–3 on NUMA node 0
numactl --cpunodebind=0 --membind=0 \
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.3-70B-Instruct --tensor-parallel-size 4
Dva súvisiace parametre:
-
kernel.numa_balancingmigruje stránky smerom k procesoru, ktorý s nimi pracuje. Pri bežných workloadoch pomáha, pri AI je občas kontraproduktívny: prefill prejde váhy raz, jadro stránky presunie a ďalšia požiadavka si ich ťahá späť. Predvolene nechajte zapnuté (1); ak jitter koreluje snuma_pte_updatesv/proc/vmstat, skústesysctl kernel.numa_balancing=0. -
NCCL na dvojsoketovom stroji — nastavte
NCCL_SOCKET_IFNAMEna manažmentovú NIC aNCCL_IB_HCAna NIC na rovnakom NUMA uzle ako GPU. Nesprávna NIC potichu zníži medziuzlovú priepustnosť na polovicu.
Jednosoketový server EPYC 9004/9005: celú túto sekciu ignorujte.
Transparent Huge Pages: madvise, nie always
THP príležitostne zlučuje 4 KB stránky do 2 MB stránok a znižuje tlak na TLB. PyTorch aj ovládač CUDA z toho mierne profitujú. Pascou je režim always pri vysokom obrate pamäte — jadro zastaví vlákno na 50–100 ms kvôli kompakcii pamäte a torpéduje akékoľvek SLO na latenciu.
Správne nastavenie je madvise: aplikácie, ktoré vedia, čo robia, zavolajú na dlhožijúce alokácie madvise(MADV_HUGEPAGE) a THP dostanú; nič iné nie. Nastavuje sa cez /etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="... transparent_hugepage=madvise"
update-grub && reboot. Defrag by mal sedieť — defer+madvise umožní, aby kompakcia prebiehala na pozadí, a nie v ceste alokujúceho vlákna:
echo defer+madvise | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
Pre vLLM / SGLang / TensorRT-LLM v produkcii je madvise správna predvoľba. PyTorch s CUDA Unified Memory (výskumný kód) občas ťaží z always — pred prepnutím merajte.
Explicitné 1 GB hugepages: len ak ste merali
Explicitné hugepages rezervované pri štarte cez hugetlbfs prinášajú pri veľmi veľkých LLM váhach malý dodatočný zisk tým, že eliminujú výpadky TLB pri prechode váh. Háčik: pamäť sa rezervuje dopredu a je nedostupná pre zvyšok systému, aplikácia musí byť na jej využitie postavená a zisk je pri väčšine servovacích workloadov 1–3 %.
GRUB_CMDLINE_LINUX_DEFAULT="... default_hugepagesz=1G hugepagesz=1G hugepages=64"
Oplatí sa to pri TensorRT-LLM stroji, kde sa engine zmestí do pinnutej pamäte a vy naháňate posledné percento TTFT. Neoplatí sa to pri bežnom vLLM serveri, ktorý prepína modely, ani pri tréningovom stroji. Pri prvom builde to preskočte; vráťte sa k tomu, len ak perf stat -e dTLB-load-misses ukáže tlak na TLB a máte rezervu v RAM.
vm.max_map_count and nofile: steny, na ktoré vLLM pri škálovaní naráža
Predvolený vm.max_map_count je na Ubuntu 65530 — je to počet samostatných pamäťových mapovaní, ktoré proces môže držať. vLLM servujúci veľký model pri vysokej konkurencii alebo ľubovoľný framework používajúci množstvo mmap-ovaných safetensors shardov tento limit prekročí a spadne s hláškou Cannot allocate memory. Hodnota 262144 prevzatá z Elasticsearchu je holé minimum; pre vLLM servujúci 70B+ so stovkami súbežných sekvencií choďte na 1048576. Nič to nestojí — je to mäkký limit, nie rezervácia.
Predvolený ulimit -n je na Ubuntu 1024. Komicky málo: vLLM, Triton, workeri PyTorch DataLoadera, NCCL aj gRPC vrstva medzi nimi otvárajú stovky deskriptorov každý. Keď na limit narazíte, dostanete EMFILE: too many open files a proces, ktorý sa potichu zasekne.
# /etc/sysctl.d/99-ai-server.conf
vm.max_map_count = 1048576
# /etc/security/limits.d/99-ai-server.conf
* soft nofile 1048576
* hard nofile 1048576
# /etc/systemd/system.conf.d/99-limits.conf
[Manager]
DefaultLimitNOFILE=1048576
sudo sysctl --system a systemctl daemon-reexec. Overte cez ulimit -n a cat /proc/<pid>/limits.
Afinita IRQ: pripnutie prerušení sieťovky na lokálne jadrá
Na 100 GbE NIC ConnectX-6/7, ktorá trvale ťahá 10+ GB/s streamovania datasetu alebo RDMA prevádzky, musia prerušenia pristávať na jadrách (a) na rovnakom NUMA uzle ako NIC a (b) nie na tých istých jadrách, kde bežia workeri DataLoadera. Predvolený irqbalance to zvláda obstojne; pri ťažkej záťaži už nie.
Najčistejším riešením je Mellanox set_irq_affinity.sh z balíka mlnx-tools:
sudo systemctl disable --now irqbalance
sudo /usr/sbin/set_irq_affinity.sh enp1s0f0 # all NIC-local cores
sudo /usr/sbin/set_irq_affinity_cpulist.sh 4-11 enp1s0f0 # pin to specific cores
Chybou je nechať bežať irqbalance na stroji, kde ste zároveň pripínali ručne — budú si navzájom prekážať. Vyberte si jedno. Servovací stroj s jednou či dvoma RDMA sieťovkami: pripnite ručne a vypnite irqbalance. Univerzálny stroj s mnohými rozhraniami: nechajte irqbalance zapnutý a kritickú NIC vylúčte cez --banirq.
Sieťové buffery jadra: len pre rýchle úložisko a streamovacie cesty
Pri jednouzlovom inferenčnom serveri, ktorý komunikuje s klientmi cez obyčajné TCP pri miernych rýchlostiach, je predvolených 208 KB pre net.core.rmem_max / wmem_max úplne v poriadku. Pri uzle, ktorý ťahá tréningové dáta zo 100 GbE NFS alebo objektového úložiska, alebo pri vLLM frontende za load balancerom s vysokým RPS sú predvolené hodnoty stropom vášho súčinu šírky pásma a oneskorenia. Východiskové nastavenie pre stroj pripojený cez 100 GbE:
# /etc/sysctl.d/99-ai-network.conf
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.ipv4.tcp_rmem = 4096 87380 268435456
net.ipv4.tcp_wmem = 4096 65536 268435456
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 65535
Maximum 256 MB je pre 100 GbE v rámci racku prehnané (BDP je ~1.25 MB), ale neškodí — automatické ladenie TCP zväčšuje buffery len podľa potreby až po tento strop. Pri RDMA (RoCE / IB) na dátovej ceste tieto parametre nehrajú rolu — RDMA obchádza TCP stack jadra. Stále však platia pre manažmentovú rovinu, NFS a sťahovanie modelov.
C-stavy: latencia verzus pokojová spotreba
Na servovacom stroji citlivom na latenciu je po governore CPU najlacnejším zlepšením koncovej latencie vypnutie hlbokých C-stavov. Vstup do C6 a výstup z neho trvá desiatky mikrosekúnd; pri požiadavke, ktorá má odpovedať do 30 ms, pridáva procesor prebúdzajúci sa z C6 kvôli vzorkovaniu po tokene citeľný jitter.
GRUB_CMDLINE_LINUX_DEFAULT="... intel_idle.max_cstate=1 processor.max_cstate=1"
update-grub && reboot. Overte cez cpupower idle-info — dostupné by mali byť len C0 a C1. Cena: 30–50 W pokojovej spotreby na soket, pretože jadrá nikdy neprejdú do hlbokého spánku. Na dvojsoketovom serveri s 8 GPU je to 60–100 W trvalej réžie — zanedbateľné oproti 3.5–4.5 kW trvalého odberu GPU pod záťažou.
Použite na inferenčné servery citlivé na latenciu a na robotické cesty v reálnom čase. Preskočte na tréningových strojoch (latencia tam nehrá rolu a pokojová spotreba sa za mesiace nasčíta) a pri dávkových úlohách.
Ladenie jadra súvisiace so súborovým systémom (ochutnávka L04)
Význam má hŕstka sysctlov fs.*:
fs.aio-max-nr = 1048576 # default 65536 too low for vLLM weight loaders
fs.inotify.max_user_watches = 524288 # tooling that watches checkpoint dirs
fs.aio-max-nr je ten, ktorý štípe — frameworky robiace asynchrónne I/O proti mnohým shardom (DALI, načítavače váh vLLM) pri veľkých modeloch prejedia predvolených 65536. Samotná voľba súborového systému (XFS vs ZFS vs ext4), možnosti pripojenia a sémantika O_DIRECT sú témou L04.
Vypnutie nepotrebných modulov jadra
Bezhlavý servovací stroj nemá dôvod načítavať moduly pre bluetooth, zvuk, webkameru, bezdrôt, joystick ani tlačový server. Každý z nich je ďalší kód v ploche útoku a malé zdržanie pri štarte. Jednotlivo zanedbateľné; dohromady sa čistejší stroj oveľa lepšie chápe.
# /etc/modprobe.d/blacklist-ai-server.conf
blacklist bluetooth
blacklist btusb
blacklist snd_hda_intel
blacklist uvcvideo
blacklist joydev
# Plus
sudo systemctl disable --now bluetooth.service cups.service avahi-daemon.service \
ModemManager.service whoopsie.service apport.service
Štart klesne z ~30 s na ~10 s na typickom EPYC builde a lsmod sa stane čitateľným. Nulový dopad na výkon inferencie, mierne zmenšenie plochy útoku.
Dva profily: inferencia verzus tréning
Tvar ladenia je naozaj odlišný. Náčrty na priame použitie:
Inferencia (/etc/sysctl.d/99-ai-inference.conf)
vm.max_map_count = 1048576
vm.swappiness = 1
vm.overcommit_memory = 1
kernel.numa_balancing = 1
fs.aio-max-nr = 1048576
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.ipv4.tcp_rmem = 4096 87380 268435456
net.ipv4.tcp_wmem = 4096 65536 268435456
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 65535
Navyše na príkazovom riadku jadra: transparent_hugepage=madvise intel_idle.max_cstate=1 processor.max_cstate=1. Governor: performance. IRQ sieťovky pripnuté na NUMA-lokálne jadrá, irqbalance vypnutý.
Tréning (/etc/sysctl.d/99-ai-training.conf)
vm.max_map_count = 1048576
vm.swappiness = 1
vm.overcommit_memory = 1
kernel.numa_balancing = 0 # page migration mid-epoch is just churn
fs.aio-max-nr = 1048576
fs.inotify.max_user_watches = 524288
net.core.rmem_max = 536870912
net.core.wmem_max = 536870912
net.ipv4.tcp_rmem = 4096 131072 536870912
net.ipv4.tcp_wmem = 4096 131072 536870912
Príkazový riadok jadra: transparent_hugepage=madvise (pripínanie C-stavov preskočte — tréning je limitovaný priepustnosťou a pokojová spotreba sa za týždne nasčíta). Governor: performance, ak máte energetický rozpočet. NCCL pripnuté na lokálny NUMA cez NCCL_IB_HCA.
Pasca „všetko som vyladil a je to pomalšie“
Najčastejším spôsobom zlyhania je kargokultový výsyp: skopírovať sto sysctlov z blogu, rebootovať, pozorovať horší výkon a netušiť, ktorý parameter vrátiť späť. Metóda, ktorá funguje:
- Stanovte si základňu —
nccl-tests all_reduce_perf, vLLMbenchmark_throughput.py, čas tréningového kroku. Čísla si zapíšte. - Zmeňte jednu vec.
- Spustite rovnaký benchmark znova, trikrát kvôli šumu.
- Ponechajte, ak je medián citeľne lepší. Inak vráťte späť.
- Zmenu a jej dôvod zdokumentujte vo verziovacom systéme vedľa súboru so sysctlmi.
Videli sme produkčné stroje so 40 riadkami sysctl ladenia, ktoré boli dohromady o 2 % pomalšie než predvolené Ubuntu — každá jednotlivá zmena neutrálna alebo horšia, no operátor si bol istý, že „ladenie pomáha“.
Red Hat tuned 2.27 prináša explicitné profily ai-inference a ai-training a na RHEL / Rocky je to dôveryhodná skratka. Ubuntu ho nedodáva; apt install tuned funguje, ale je menej dotiahnutý. Na Ubuntu sa ručne písané drop-in súbory v /etc/sysctl.d/ a čistý príkazový riadok GRUB auditujú ľahšie a presne viete, čo je nastavené.
Čo robiť ďalej
Rozumná postupnosť ladenia jadra pre nový build Kentino AI, v poradí podľa priority:
-
Nastavte governor CPU na
performance. Overte cezcpupower frequency-info. Najväčšia výhra zadarmo. -
Spustite
numactl --hardwareanvidia-smi topo -m. Pred akýmkoľvek pripínaním pochopte topológiu. Na dvojsoketových strojoch naplánujte, ktoré GPU patria ku ktorému NUMA uzlu. -
Nastavte limity
vm.max_map_countanofile. Tieto nezabraňujú pomalosti, ale zlyhaniam. Urobte ich pred prvým produkčným behom. -
Nastavte
transparent_hugepage=madvisena príkazovom riadku jadra. -
Pripnite IRQ sieťovky na NUMA uzol lokálny k NIC pomocou
set_irq_affinity.sh. Vypniteirqbalance, ak ste tak urobili. -
Len pre inferenčné stroje: vypnite hlboké C-stavy cez
intel_idle.max_cstate=1 processor.max_cstate=1. - Sieťové buffery ladite len vtedy, ak ste namerali úzke miesto na úložisku alebo streamovacej ceste 25/100 GbE.
- Explicitné 1 GB hugepages preskočte, pokiaľ nemáte benchmark preukazujúci tlak na TLB.
- Pred každou zmenou aj po nej benchmarkujte. Vždy len jednu vec naraz. Dokumentujte.
Krížové odkazy: L01 pre ovládač a pripnutie jadra, L02 pre CUDA / kontajnerový stack, L04 pre vrstvu súborového systému, L05 pre monitoring.
Ladenie jadra na modernom Linuxe je väčšinou jednorazová konfigurácia, nie nekonečná optimalizačná hra. Nastavte správne governor, umiestnenie NUMA a limity. Zvyšok preskočte, pokiaľ váš benchmark nehovorí inak.
Toto je súčasť Kentino Wiki, referenčnej série o AI compute, robotike a systémoch, ktoré ich spájajú. Komentáre a opravy uvítame na info@kentino.com.