Linux Kernel Tuning til AI-servere: Hvad der rent faktisk sætter gang i udviklingen

Standard Ubuntu-kernen på en 6.8 eller 6.11 HWE-stak er fin til omkring 80% af AI-arbejdsbelastninger. Denne artikel er til de andre 20% - multi-socket EPYC, der driver otte GPU'er, en vLLM-serverboks, der rammer en vm.max_map_count væg under belastning, eller en inferensserver hvis halelatens bliver spist af CPU'ens C-tilstandsovergange.

De fleste offentlige råd om "Linux-tuning til AI" er genbrugte databasevejledninger fra 2015, og en ikke-triviel del af dem forværrer stille og roligt inferensforsinkelsen. Nedenfor er det mindre, ærlige sæt af ændringer, der rent faktisk hjælper på en Kentino AI-boks i Kentino-klassen (4-8 GPU'er på Xeon eller EPYC, Ubuntu 22.04 / 24.04, kernel 6.x). Driver og CUDA-stak antages at være installeret pr. L01 og L02.

Det ærlige hierarki af sejre

Før nogen sysctl-ændringer, ankre på præmiens størrelse:

Skift Typisk sejr på inferens/træning
NUMA-bevidst procesplacering 10–30 % på multistikdåser
CPU-regulator performance (Fra powersave) 5-15% på serveringsæsker, lavere TTFT
Deaktivering af dybe C-tilstande Mikrosekunder fra P99, +30–50 W tomgang / stikkontakt
THP indstillet til madvise 1-5% på PyTorch, færre stop under churn
vm.max_map_count, ulimit -n Forhindrer, at serveringen vælter under belastning
IRQ-affinitet til lokal NUMA-node 5–15 % på netværksforbundne DataLoader/RDMA-stier
TCP-bufferstørrelse (rmem_max, wmem_max) Kun relevant for >25 GbE lagring/streaming
Alt andet 0–3%, ofte støj

Hvis du ikke tager andet med dig fra denne artikel: NUMA-bevidsthed og CPU-regulatoren er de to store gevinster. Alt andet er afrundingsfejl, medmindre du har målt.

CPU-regulator: de billigste 5-15% på bordet

Ubuntu bruger standardindstillingen powersave (intel_pstate) eller ondemand (acpi_cpufreq) afhængigt af driveren. Begge ramper clocker reaktivt, hvilket koster 5-15% på inferens-TTFT og på CPU-sidens forberedelse omkring et vLLM forward pass - tokenisering, planlægning, sampling-logits.

Til en serveringsæske, sæt performance og glem:

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

På EPYC Genoa / Torino med kerne 6.5+, den amd-pstate chaufføren erstatter acpi-cpufreqSamme idé, sæt scaling_governor til performance. Den nyere amd-pstate-epp tilstand bevarer ydeevnen, samtidig med at den tillader boosting pr. kerne; behold EPP på performance, Ikke balance_performance.

Advarsel: På en træningsboks med vedvarende ~100% GPU-udnyttelse bliver CPU'erne alligevel belastet, og regulatoren betyder mindre. Den store gevinst er på inferensservere, der går i dvale mellem anmodninger og skal rampe øjeblikkeligt.

NUMA: forskellen på, om en EPYC-boks er hurtig eller middelmådig

Single-socket Xeon- eller EPYC-servere har intet at tænke på – én NUMA-node, al hukommelsesadgang er lokal. Dual-socket-bokse er der, hvor 10-30% bor.

Standardplanlæggeren vil med glæde placere en vLLM-worker på socket 0 og få den til at sende en page-fault til hukommelsen allokeret på socket 1 — hver indlæsning er et cross-socket UPI / Infinity Fabric hop. For en model med hundredvis af GB vægte, der går én gang pr. token, er dette reelt.

Inspicere:

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

Det mønster, du ønsker – og som NCCL registrerer automatisk – er GPU N, der er fastgjort til NUMA-noden, der er vært for dens PCIe-rodkompleks, ikke den anden socket. For en manuelt startet inferensproces skal du fastgøre både CPU og hukommelse til den lokale node:

# 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

To relaterede knapper:

  • kernel.numa_balancing migrerer sider mod CPU'en, der berører dem. Nyttig til generelle arbejdsbelastninger, lejlighedsvis kontraproduktiv for AI: en forudfyldning flytter vægte én gang, kernen migrerer sider, og den næste anmodning trækker dem tilbage. Lad være aktiveret som standard (1); hvis jitter korrelerer med numa_pte_updates in /proc/vmstat, prøve sysctl kernel.numa_balancing=0.
  • NCCL på dobbeltstik — sæt NCCL_SOCKET_IFNAME til ledelsens NIC og NCCL_IB_HCA til NIC'et på den samme NUMA-node som GPU'erne. Forkert NIC halverer lydløst gennemløbshastigheden mellem noder.

EPYC 9004/9005-server med én sokkel: Ignorer hele dette afsnit.

Gennemsigtige store sider: madvise, Ikke always

THP folder 4 KB sider opportunistisk til 2 MB sider, hvilket reducerer TLB-trykket. PyTorch og CUDA-driveren drager kun beskeden fordel. Fælden er always tilstand under hukommelsesturn — kernen stopper en tråd til 50-100 ms komprimering af hukommelse, hvilket torpederer enhver latenstids-SLO.

Den rigtige indstilling er madvise: applikationer, der ved, hvad de laver, kalder madvise(MADV_HUGEPAGE) på langlivede allokeringer og få THP; intet andet gør det. Sæt via /etc/default/grub:

GRUB_CMDLINE_LINUX_DEFAULT="... transparent_hugepage=madvise"

update-grub && rebootDefragmentering skal matche — defer+madvise lader komprimering ske i baggrunden i stedet for i den allokerende tråds sti:

echo defer+madvise | sudo tee /sys/kernel/mm/transparent_hugepage/defrag

For vLLM / SGLang / TensorRT-LLM i produktion, madvise er den rigtige standard. PyTorch med CUDA Unified Memory (forskningskode) drager nogle gange fordel af always — mål før du vender.

Eksplicitte 1 GB hugepages: kun hvis du har målt

Eksplicitte hugepages reserveret ved opstart via hugetlbfs giver en lille ekstra gevinst for meget store LLM-vægte ved at eliminere TLB-fejl på vægtscanningen. Fangsten: hukommelse er reserveret på forhånd og ikke tilgængelig for resten af ​​systemet, applikationen skal bygges til at bruge dem, og gevinsten er 1-3% for de fleste serverarbejdsbelastninger.

GRUB_CMDLINE_LINUX_DEFAULT="... default_hugepagesz=1G hugepagesz=1G hugepages=64"

Det er det værd for en TensorRT-LLM-boks, hvor motoren passer i fastlåst hukommelse, og du jagter den sidste procent af TTFT. Ikke det værd for en generel vLLM-server, der bytter modeller, eller nogen form for træningsboks. Spring den første build over; genbesøg kun hvis perf stat -e dTLB-load-misses viser TLB-tryk, og du har RAM-headroom.

vm.max_map_count og nofileVæggene vLLM rammer i stor skala

Standard vm.max_map_count på Ubuntu er 65530 — et antal forskellige hukommelseskortlægninger, som en proces kan indeholde. vLLM betjener en stor model med høj samtidighed, eller ethvert framework, der bruger mange mmap'd safetensors skår, blæser forbi dette og dør med Cannot allocate memoryDen Elasticsearch-afledte værdi på 262144 er det absolutte minimum; for vLLM, der betjener 70B+ med hundredvis af samtidige sekvenser, skub til 1048576. Koster ingenting - soft limit, ikke en reservation.

Standard ulimit -n på Ubuntu er 1024. Komisk lav: vLLM, Triton, PyTorch DataLoader-arbejdere, NCCL og gRPC-laget mellem dem åbner hundredvis af FD'er hver. Nå grænsen, og du får EMFILE: too many open files og en proces, der kiler sig lydløst sammen.

# /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 og systemctl daemon-reexecBekræft med ulimit -n og cat /proc/<pid>/limits.

IRQ-affinitet: fastgørelse af NIC-afbrydelser til lokale CPU'er

På et 100 GbE ConnectX-6/7 NIC, der understøtter 10+ GB/s datasætstreaming eller RDMA-trafik, skal afbrydelser lande på CPU'er (a) på den samme NUMA-node som NIC'et, og (b) ikke de samme kerner, der kører DataLoader-workers. Standardindstillingen irqbalance gør et acceptabelt stykke arbejde; under tung belastning gør den ikke.

Den reneste løsning er Mellanox' set_irq_affinity.sh fra 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

Det forkerte træk er at forlade irqbalance kører på en boks, hvor du også har fastgjort manuelt — de kæmper. Vælg én. Serveringsboks med en eller to RDMA NIC'er: fastgør manuelt, deaktiver irqbalanceUniversalboks med mange grænseflader: lad være irqbalance på med --banirq for at udelukke det kritiske NIC.

Netværkskernebuffere: kun til hurtig lagring/streamingstier

For en inferensserver med én node, der kommunikerer med klienter via almindelig TCP med moderate hastigheder, er standardværdien net.core.rmem_max / wmem_max på 208 KB er fint. For en node, der henter træningsdata fra 100 GbE NFS eller objektlager, eller en vLLM-frontend bag en load balancer med høj RPS, er standardindstillingerne loftet på dit båndbreddeforsinkelsesprodukt. Et startsæt for en 100 GbE-tilsluttet boks:

# /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

256 MB maks. er overkill for in-rack 100 GbE (BDP er ~1.25 MB), men harmløst — TCP auto-tuning øger kun buffere efter behov op til grænsen. På RDMA (RoCE / IB) for datastien er disse knapper ligegyldige — RDMA omgår kernens TCP-stakk. De er stadig vigtige for administrationsplanet, NFS og modeldownloads.

C-tilstande: latenstid vs. tomgangsstrøm

For en latenstidsfølsom serveringsboks er den billigste forbedring af halelatensen efter CPU-regulatoren at deaktivere dybe C-tilstande. C6-indgang/udgang er i titusindvis af mikrosekunder; for en anmodning, der skal besvares på 30 ms, tilføjer en CPU, der kommer ud af C6 for at håndtere post-token sampling, mærkbar jitter.

GRUB_CMDLINE_LINUX_DEFAULT="... intel_idle.max_cstate=1 processor.max_cstate=1"

update-grub && rebootBekræft med cpupower idle-info — kun C0 og C1 bør være tilgængelige. Omkostninger: 30-50 W tomgangseffekt pr. stikkontakt fordi kerner aldrig går i dyb dvale. På en server med 8 GPU'er og dual-sockets er 60-100 W permanent overhead trivielt mod 3.5-4.5 kW vedvarende GPU-forbrug under belastning.

Anvend på latenstidsfølsomme inferensservere og realtidsrobotstier. Spring træningsbokse over (latenstid spiller ingen rolle, inaktiv strøm akkumuleres over måneder) og batchjob.

Filsystemrelateret kernejustering (forhåndsvisning af L04)

En håndfuld af fs.* sysctls betyder noget:

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 er den der bider — frameworks der udfører asynkron I/O mod mange shards (DALI, vLLM weight loaders) brænder igennem standard 65536 på store modeller. Valg af filsystem i sig selv (XFS vs ZFS vs ext4), monteringsmuligheder og O_DIRECT semantikken lever i L04.

Deaktivering af unødvendige kernemoduler

En headless serverboks behøver ikke at indlæse moduler til Bluetooth, lyd, webcam, trådløst netværk, joystick eller printerserveren. Hver af dem kræver mere kode i angrebsfladen og en lille opstartsforsinkelse. Individuelt trivielle; samlet set er en renere boks lettere at ræsonnere med.

# /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

Opstartstiden falder fra ~30 sek. til ~10 sek. på en typisk EPYC-build, og lsmod bliver læsbar. Ingen påvirkning af inferenspræstation, beskeden reduktion af angrebsfladen.

To profiler: inferens vs. træning

Stemmeformen er virkelig anderledes. Drop-in skitser:

Inferens (/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

Plus på kernelkommandolinjen: transparent_hugepage=madvise intel_idle.max_cstate=1 processor.max_cstate=1Guvernør: performanceNIC IRQ'er fastgjort til NUMA-lokale kerner, irqbalance af.

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

Kernel-kommandolinje: transparent_hugepage=madvise (spring C-state pinning over — træning er gennemløbsbundet, tomgangsstrøm akkumuleres over uger). Governor: performance hvis du har strømbudgettet. NCCL fastgjort til lokal NUMA via NCCL_IB_HCA.

"Jeg justerede alt, og det blev langsommere"-fælden

Den mest almindelige fejltilstand er cargo-cult dump: kopier hundrede sysctl'er fra et blogindlæg, genstart, observer dårligere ydeevne, ingen idé om hvilken knap man skal bakke ud. Metode der virker:

  1. Etabler en basislinje — nccl-tests all_reduce_perf, vLLM benchmark_throughput.py, træningstrin tid. Skriv tallene ned.
  2. Ændr én ting.
  3. Kør den samme benchmark igen tre gange for støj.
  4. Behold hvis medianen er betydeligt bedre. Vend tilbage hvis ikke.
  5. Dokumenter ændringen og hvorfor i versionsstyringsprogrammet ved siden af ​​sysctl-filen.

Vi har set produktionsbokse med 40 linjer sysctl-tuning, der tilsammen var 2% langsommere end Ubuntus standardindstillinger — hver enkelt ændring var neutral eller værre, men operatøren var sikker på, at "tuningen hjælper".

Red Hats tuned 2.27 eksplicitte skibe ai-inference og ai-training profiler og er en troværdig genvej på RHEL / Rocky. Ubuntu leverer den ikke; apt install tuned fungerer, men er mindre poleret. På Ubuntu, håndrullede drop-ins i /etc/sysctl.d/ og en ren GRUB-kommandolinje er nemmere at revidere, og du ved præcis, hvad der er indstillet.

Hvad skal jeg gøre næste

En rimelig kernejusteringssekvens for en ny Kentino Kentino AI-build, i prioriteret rækkefølge:

  1. Indstil CPU-regulatoren til performance. Bekræft med cpupower frequency-infoDen største enkeltstående gevinst uden omkostninger.
  2. Kør numactl --hardware og nvidia-smi topo -m. Forstå topologien, før du fastlåser noget. På bokse med dobbelt sokkel skal du planlægge, hvilke GPU'er der skal bruges til hvilken NUMA-node.
  3. sæt vm.max_map_count og nofile grænser. Disse forebygger fejl, ikke langsommelighed. Gør dem før den første produktionskørsel.
  4. sæt transparent_hugepage=madvise på kernens kommandolinje.
  5. Pin NIC IRQ'er til NIC'ens lokale NUMA-node med set_irq_affinity.sh. Deaktiver irqbalance hvis du har gjort det.
  6. Kun for inferensbokse: deaktiver dybe C-tilstande via intel_idle.max_cstate=1 processor.max_cstate=1.
  7. Juster netværksbuffere kun hvis du har målt en flaskehals på en 25/100 GbE-lagrings- eller streamingsti.
  8. Spring 1 GB eksplicitte hugesider over medmindre du har en benchmark, der viser TLB-tryk.
  9. Benchmark før og efter hver ændring. Én ting ad gangen. Dokumentér.

Krydsreferencer: L01 til driver- og kernel-pinning, L02 til CUDA/container-stakken, L04 til filsystemlaget, L05 til overvågning.

Kernel-tuning på moderne Linux er for det meste engangskonfiguration, ikke et løbende optimeringsspil. Få governor, NUMA-placering og grænser korrekte. Spring resten over, medmindre dit benchmark siger andet.


Dette er en del af Kentino Wiki, en referenceserie om AI-beregning, robotteknologi og de systemer, der forbinder dem. Kommentarer og rettelser er velkomne på info@kentino.com.