TCP Small Queues begrænser antallet af udestående bytes i Linux-sendestien pr. TCP-flow og reducerer dermed Forsinkelse samt bufferbloat målrettet ned. Jeg viser, hvordan denne mekanisme i Linux-netværk Stack viser, hvordan jeg fastsætter fornuftige grænser, og hvilke interaktioner der opstår med pacing, QDiscs og congestion control.
Centrale punkter
- Per-flow-grænse: TSQ begrænser antallet af udestående bytes pr. TCP-socket.
- Mindre bufferbloat: Kortere køer reducerer RTT.
- Modtryk: Applikationer skriver langsommere, når grænsen træder i kraft.
- Retfærdighed: Ingen enkelt flow optager hele køerne.
- Adaptiv Styring: Grænsen afhænger af hastigheden og segmentstørrelsen.
Sådan fungerer TCP Small Queues
TSQ tager over der, hvor TCP-segmenterne slutter QDisc og driveren. Når jeg skriver data til en socket, kontrollerer kernen før hver enqueue de allerede allokerede bytes til denne strøm. Når strømmen når grænsen, markerer logikken socketen som begrænset og stopper yderligere enqueue. Først når netværkskortet frigiver bufferen, må soklen sende igen, og jeg kan igen skubbe data ind i stakken. Dette stramme modtryk holder Køer kort og gør reaktionstiderne mere forudsigelige.
Hvorfor lange køer forlænger responstiden
Opret store driver- og QDisc-køer Bufferbloat, især med TSO/GSO og store datamængder. En stor download kan fylde udgangskøerne, mens interaktive processer som SSH, API-kald eller VoIP må vente i kø. Den overfyldte kø dominerer så RTT i stedet for den egentlige link-tid. Congestion Control reagerer langsomt, fordi ACK’er ankommer for sent, og træffer dermed dårligere cwnd-beslutninger. TSQ begrænser antallet af forhåndsbufferede bytes pr. flow, så små, tidskritiske pakker hurtigt kommer ud på linjen.
Et kig bag kulisserne: hvad kernen tæller
Under overfladen tæller kernen ikke „pakker“, men bytes – mere præcist: de hukommelses-bytes, som soklen allerede har sat i køen. Det afgørende er, hvad stakken indeholder af skbuff-strukturer samt truesize har allokeret, og som endnu ikke er blevet behandlet af NIC’en. TSQ kobler en Gasspjæld åbent/lukket‑Spor: Hvis en socket når kreditgrænsen, sætter stakken et begrænsningsflag og kalder først igen, når TX-operationer er afsluttet (NAPI/IRQ) write_space() for at applikationen kan sende igen. Denne tilbagemelding er hurtigere end rent tabbaserede signaler fra overbelastningskontrol og virker før QDisc. Med TSO/GSO forbliver mekanismen effektiv, fordi grænsen ved før baseret på det byte-budget, der ligger til grund for segmenteringen: Store super-frames slippes kun ind i QDisc, hvis der er tilstrækkelig ledig kapacitet, hvilket begrænser bursts.
Dynamiske grænser og tempo
Jeg drager fordel af TSQ, fordi grænsen ikke forbliver statisk, men i stedet Vurder og segmentstørrelse. Målet er cirka en millisekund data i sendebanen pr. flow, uanset om der er tale om 100 Mbit, 1 Gbit eller 10 Gbit. Ved hurtige forbindelser stiger den tilladte byte-kredit, mens den falder ved langsomme forbindelser. I kombination med TCP-pacing forbliver bursts små, og ACK’er kommer hurtigere tilbage. På den måde opnår jeg mærkbart lavere Toppen af ventetiden, uden at begrænse gennemstrømningen unødigt.
Interaktion mellem Per-Socket og appen
TSQ virker kun, hvis applikationen også registrerer modtrykket. Derfor tager jeg højde for indstillinger som SO_SNDBUF, TCP_NOTSENT_LOWAT og autocorking. Et for stort sendebuffervindue kan på kort sigt skubbe mange bytes ind i stakken; TSQ bremser ganske vist, men appen bemærker det først, når send() blokeret eller returnerer EAGAIN. Med TCP_NOTSENT_LOWAT trækker jeg den „ikke-afsendte“ del tilbage i userland og supplerer dermed TSQ på kernelsiden. Autocorking (eller eksplicit TCP_CORK/MSG_MORE) hjælper med at samle små skrivninger uden at forårsage spidsbelastninger i latenstiden. Pacing-grænser pr. socket (f.eks. via SO_MAX_PACING_RATE) passer godt sammen med TSQ: Frekvensen udjævner tidsmæssigt, mens bytegrænsen begrænser rumligt. Vigtigt: TCP_NODELAY Deaktiverer Nagle og kan øge interaktiviteten, men uden TSQ stiger risikoen for burst; med TSQ har jeg begge dele under kontrol.
Praktisk vejledning: relevante TSQ-værdier
Jeg fastlægger de overordnede rammer med net.ipv4.tcp_limit_output_bytes (Sysctl). De sædvanlige standardværdier ligger på omkring 128–262 KB pr. flow. Til mange web- og API-arbejdsbelastninger vælger jeg lavere værdier, så de interaktive svar forbliver hurtige. Til sikkerhedskopiering eller replikering hæver jeg grænsen moderat, så længe RTT forbliver stabil. Hvis du vil dykke dybere ned i kø-siden, finder du grundlæggende oplysninger om Pakkekøer på serveren, som hjælper med klassificeringen.
| Scenarie | Linkfrekvens | Vejledende værdi for tcp_limit_output_bytes | Mål |
|---|---|---|---|
| API/HTTP meget interaktivt | 100 Mbit – 1 Gbit | 64–128 KB | lav RTT, korte pigge |
| Blandet belastning: Web + downloads | 1–10 Gbit | 128–256 KB | Balance fra Gennemstrømning og latenstid |
| Replikering/sikkerhedskopiering | 1–10 Gbit | 256–512 KB | konstant bulk-flow, acceptabel Forsinkelse |
| WAN med høj RTT | 10–100 Mbit | 96–192 KB | mindre bursts, mere retfærdige Stikord |
QDisc og overbelastningskontrol i samspil
TSQ arbejder ved indgangen til QDisc, mens algoritmer som fq_codel styrer overbelastningen på forbindelsen. Sammen reducerer de køerne og sikrer en retfærdig fordeling. Med TCP BBR får jeg yderligere fordele, fordi mere realistiske RTT-målinger fører til bedre pacing og cwnd-styring. CUBIC reagerer også mere jævnt, når jeg fjerner for lange køtider. På den måde stiger gennemstrømningen organisk, mens Svartid forbliver under kontrol.
Virtualisering og cloud-stacks
I virtuelle maskiner (VM’er) lægger flere bufferniveauer sig sammen: gæst-QDisc, virtio/vhost-køer, vært-QDisc og det fysiske netværkskort. Jeg holder TSQ aktivt i gæsten og vælger der en konservativ grænse, så der ikke kommer store bursts ind i værten. På hypervisoren sikrer jeg korte latenstidskæder ved hjælp af fair QDiscs, moderate TX-ringe og korrekt IRQ-pinning. SR-IOV kan reducere latenstiden, men flytter ansvaret over på gæsterne: Uden TSQ i gæsten risikerer man lange VF-køer. I containere griber TSQ pr. NetNS som sædvanlig; ved hjælp af cgroup-pacing og CPU-begrænsninger forhindrer jeg, at en støjende nabo indirekte øger latenstiden. Det er også vigtigt at holde øje med coalescing og offloads i virtio-stien: Overdreven samling forlænger ACK'er, mens for lidt samling mindsker effektiviteten – jeg justerer i forhold til latenstidsmålet, ikke dogmatisk.
Wi-Fi og indlejrede systemer: Håndtering af særlige tilfælde
På WLAN-forbindelser er det Sammenlægning i MAC-laget. Hvis jeg tillader for få bytes i sendebanen, kan driveren samle færre rammer, hvilket mindsker effektiviteten. I sådanne opsætninger øger jeg grænsen forsigtigt og kontrollerer aggregeringsgraden. OpenWrt- og indlejrede platforme drager desuden fordel af slanke stier i drivere og færre atomare operationer. Jeg tester hver tilpasning under reel trådløs belastning, før jeg Profil ruller bredt ud.
Overvågning og målinger, der virkelig tæller
Jeg observerer RTT‑Fordeling pr. socket, og jeg kigger på afvigelser, ikke kun på gennemsnitsværdier. Med ss, tc og eksportører udlæser jeg kø-længder, genudsendelser samt pacing_rate. eBPF-programmer leverer begivenheder til mig, når sockets bliver begrænset og frigives igen. Time-to-First-Byte og 95./99. percentil viser, om TSQ virker efter hensigten. Uden måleværdier forbliver enhver Optimering en blindflyvning.
A/B-tests og belastningstests med statistisk signifikans
Jeg måler TSQ-effekter på en reproducerbar måde: Først en baseline uden ændringer, derefter isolerede parametersweeps (f.eks. 64, 96, 128, 192 KB). Ved blandede arbejdsbelastninger kører jeg parallelle strømme (bulk + mange korte anmodninger) og sammenligner 95./99. percentilen for latenstider, ikke kun medianen. Selv hvis jeg afbryder testkørslerne tydeligt (opvarmning, målevindue, nedkøling), forbliver artefakterne synlige. Jeg lægger vægt på konstante faktorer: samme payload-mønstre, identisk rute/MTU, identiske server- og klient-CPU-frekvenser. På WAN-forbindelser simulerer jeg forsinkelse/jitter/tab med tc netem, for at kontrollere, om TSQ-grænserne ikke sætter et loft for tidligt ved høj BDP. Først når percentilerne bliver smallere, og antallet af retransmissioner og tab forbliver stabilt, overfører jeg værdierne til produktionen.
Hardware-optimering og driveroplysninger
Jeg tjekker TSO/GSO-indstillingerne, NIC’ens ringbuffer og IRQ-styringen, så TSQ fungerer optimalt. For store TX-ringe forlænger køen ved enheden; for små mindsker udnyttelsen. Grov samling af interrupts forsinker ACK’er, mens fin samling øger CPU-belastningen. Jeg tilpasser gennemgangen til praksis og henviser som udgangspunkt til Sammenlægning af afbrydelser. Målet er fortsat en pålidelig Forsinkelse ved en holdbar gennemstrømning.
NUMA, RSS og CPU-affinitet
Korte køer nytter ikke meget, hvis pakker hele tiden bevæger sig på tværs af NUMA-grænser. Jeg knytter RX/TX-køer via RSS/irqbalance til kerner i det samme NUMA-domæne, hvor appen også kører. Med XPS/RPS styrer jeg, hvilke CPU'er der overtager TX-arbejdet, og undgår dermed cross-socket-hopper. Færre cache-misses og mindre lock-contention hjælper indirekte TSQ: Afsluttede opgaver returneres hurtigere, soklen „aflastes“ tidligere, og der opstår ingen latenstops. Ved meget mange flows pr. vært planlægger jeg tilstrækkelige køer og undgår, at flere støjende flows kolliderer på samme TX-ring.
Trin for trin: Kontrol af, om TSQ er aktivt
Jeg begynder med et kig på Sysctl: sysctl net.ipv4.tcp_limit_output_bytes viser den aktuelle grænse. Derefter tjekker jeg ss -tin for de enkelte sockets, holder øje med send-q og rtt og sammenligner belastningsfaser med og uden justering af grænsen. Med iperf3 genererer jeg baggrundsbelastning og måler parallelt API-svarstider for at synliggøre prioriteter. tc -s qdisc giver mig antallet af pakker og tabte pakker for udgangsdisciplinen. Hvis 95./99. percentilen forbliver tæt på hinanden, og CPU‑Belastning i rammen, vælg den passende grænseværdi.
Almindelige fejl og anti-mønstre
- „Større buffer = højere ydeevne“: Det gælder for gennemstrømningstests uden et mål for latenstid, men holder ikke for interaktive tjenester. TSQ erstatter overdimensionerede køer med en behovsbaseret kredit pr. flow.
- „TSQ koster gennemstrømning“: Når det er indstillet korrekt, begrænser TSQ bursts, ikke den gennemsnitlige hastighed. Ved bulk-workloads sætter jeg grænsen moderat højt og måler percentilerne i stedet for kun spidsværdien i Mbit/s.
- „Pacing alene er nok“: Tidsudjævning er vigtig, men uden byte-begrænsning glider store GSO-rammer alligevel ind på QDisc’en. TSQ og pacing supplerer hinanden.
- „Én værdi for alle“: Workloads, links og NIC'er er forskellige. Jeg arbejder med områdeværdier og validerer for hvert miljø.
- „Kun TCP berørt“: Fokus er på TCP, men der findes andre justeringsmuligheder i systemet (f.eks. for UDP-belastning). Jeg forhindrer, at parallelle protokoller ukontrolleret tilstopper de samme køer.
Konklusion: Målrettet kontrol med latenstiden
TSQ flytter kontrollen af driverkøer over til Sokkel og reducerer dermed overbelastning direkte ved kilden. Jeg begrænser antallet af forhåndsbufferede bytes pr. flow og sikrer dermed hurtige ACK’er, lavere RTT og retfærdigt fordelte køer. I kombination med fq_codel og moderne overbelastningskontrol forbliver reaktionstiden pålidelig, selv under belastning. Særlige tilfælde inden for WLAN og indlejrede systemer håndterer jeg med tilpassede grænser og tests under realistiske forhold. Den, der overvåger nøgletallene og justerer grænserne trinvist, holder Forsinkelse konstant lavt, uden at miste unødvendig kapacitet.


