Funkcja TCP Small Queues ogranicza liczbę bajtów oczekujących w ścieżce transmisji systemu Linux dla każdego przepływu TCP, co w ten sposób zmniejsza Opóźnienie wraz z „bufferbloat” w sposób celowy w dół. Pokażę, jak ten mechanizm działa w sieci w systemie Linux Stack pokazuje, jak ustalać sensowne limity i jakie interakcje powstają w związku z Pacingiem, QDiscami i kontrolą przeciążenia.
Punkty centralne
- Limit przepływu: TSQ ogranicza liczbę bajtów oczekujących na każdy gniazdo TCP.
- Mniejsze obciążenie bufora: Krótsze kolejki skracają czas RTT.
- Ciśnienie wsteczne: Aplikacje zapisują dane wolniej, gdy limit zaczyna obowiązywać.
- Sprawiedliwość: Żaden pojedynczy strumień nie zajmuje całych kolejek.
- Adaptacyjne Sterowanie: Limit zależy od częstotliwości i wielkości segmentu.
Jak działa funkcja TCP Small Queues
TSQ wkracza do akcji w momencie, gdy TCP dołącza segmenty do QDisc i przekazuje sterownikowi. Gdy zapisuję dane do gniazda, jądro przed każdym dodaniem do kolejki sprawdza liczbę bajtów już przydzielonych dla tego strumienia. Gdy strumień osiągnie limit, logika oznacza gniazdo jako ograniczone i wstrzymuje dalsze dodawanie danych do kolejki. Dopiero gdy karta sieciowa zwolni bufor, gniazdo może ponownie wysyłać dane, a ja mogę ponownie umieścić dane w stosie. Ta ścisła kontrola przepustowości utrzymuje Kolejki krótki i sprawia, że czasy reakcji są bardziej przewidywalne.
Dlaczego długie kolejki wydłużają czas reakcji
Tworzenie dużych kolejek sterowników i QDisc Bufferbloat, zwłaszcza w przypadku TSO/GSO i dużych wolumenów transmisji. Duży plik do pobrania może zapełnić kolejki wyjściowe, podczas gdy przepływy interaktywne, takie jak SSH, wywołania API czy VoIP, trafiają na koniec kolejki. Przepełniona kolejka dominuje wówczas nad RTT zamiast rzeczywistego czasu połączenia. Mechanizm kontroli przeciążenia reaguje z opóźnieniem, ponieważ potwierdzenia (ACK) docierają z opóźnieniem, co powoduje, że podejmowane są gorsze decyzje dotyczące wartości cwnd. TSQ ogranicza liczbę bajtów buforowanych z wyprzedzeniem dla każdego strumienia, aby małe, wrażliwe na opóźnienia pakiety mogły szybko trafić do sieci.
Zajrzyjmy pod maskę: co liczy się w jądrze systemu
Pod powierzchnią jądro nie liczy „pakietów“, lecz bajty, a dokładniej: bajty pamięci już umieszczone przez gniazdo w kolejce. Decydujące znaczenie ma to, co stos zawiera w strukturach skbuff wraz z truesize przydzielono, a które nie zostało jeszcze przetworzone przez NIC. TSQ łączy się z tym Zwiększenie/zmniejszenie przepustnicy‑Ścieżka: Jeśli gniazdo osiągnie limit, stos ustawia flagę ograniczającą i wywołuje ponownie dopiero po zakończeniu transmisji (NAPI/IRQ) write_space() aby aplikacja mogła ponownie wysłać dane. To sprzężenie zwrotne działa szybciej niż sygnały oparte wyłącznie na stratach, stosowane w kontroli przeciążenia, i uruchamia się przed modułem QDisc. W przypadku TSO/GSO mechanizm ten pozostaje skuteczny, ponieważ limit na przed w oparciu o budżet bajtów przypisany do segmentacji: duże superramki są wpuszczane do QDisc tylko wtedy, gdy dostępna jest wystarczająca ilość wolnego limitu, co pozwala ograniczyć występowanie serii danych.
Limity dynamiczne i tempo
Czerpię korzyści z TSQ, ponieważ limit nie pozostaje sztywno statyczny, lecz dostosowuje się do Stawka oraz wielkość segmentu. Celem jest uzyskanie około jednej milisekundy danych w kanale transmisji na każdy strumień, niezależnie od tego, czy przepustowość wynosi 100 Mbit, 1 Gbit czy 10 Gbit. W przypadku szybszego łącza dopuszczalny kredyt bajtów rośnie, a w przypadku wolniejszego łącza maleje. W połączeniu z regulacją tempa TCP (TCP-Pacing) impulsy pozostają niewielkie, a potwierdzenia (ACK) wracają szybciej. W ten sposób uzyskuję zauważalnie mniejsze Szczyty opóźnień, nie ograniczając przy tym niepotrzebnie przepustowości.
Interakcja na poziomie gniazda i aplikacji
TSQ działa tylko wtedy, gdy aplikacja również odczuwa ciśnienie zwrotne. Dlatego biorę pod uwagę takie ustawienia, jak SO_SNDBUF, TCP_NOTSENT_LOWAT oraz autocorking. Zbyt duże okno bufora wysyłania może w krótkim czasie zepchnąć wiele bajtów do stosu; TSQ wprawdzie spowalnia działanie, ale aplikacja zauważa to dopiero wtedy, gdy send() zablokowany lub zwraca kod EAGAIN. Z TCP_NOTSENT_LOWAT wycofuję część „niewysłaną“ w przestrzeni użytkownika i uzupełniam w ten sposób TSQ po stronie jądra. Autocorking (lub jawnie TCP_CORK/MSG_MORE) pomaga grupować niewielkie operacje zapisu bez powodowania skoków opóźnień. Limity tempa na gniazdo (np. poprzez SO_MAX_TEMPO_BIEGU) współgrają z TSQ: wskaźnik wygładza dane w czasie, a limit bajtów ogranicza je w przestrzeni. Ważne: TCP_NODELAY Wyłącza Nagle’a i może zwiększyć interaktywność, ale bez TSQ wzrasta ryzyko wystąpienia burstu; dzięki TSQ mam obie te kwestie pod kontrolą.
Praktyczny przewodnik: zalecane wartości TSQ
Ogólne ramy wyznaczam za pomocą net.ipv4.tcp_limit_output_bytes (Sysctl). Typowe wartości domyślne wynoszą około 128–262 KB na przepływ. W przypadku wielu obciążeń związanych z aplikacjami internetowymi i interfejsami API wybieram niższe wartości, aby odpowiedzi interaktywne były nadal szybkie. W przypadku kopii zapasowych lub replikacji umiarkowanie zwiększam ten limit, o ile czas RTT pozostaje stabilny. Osoby, które chcą zgłębić temat kolejek, znajdą podstawowe informacje na temat Kolejki pakietów na serwerze, które pomagają w klasyfikacji.
| Scenariusz | Współczynnik linków | Wartość orientacyjna tcp_limit_output_bytes | Cel |
|---|---|---|---|
| API/HTTP – bardzo interaktywne | 100 Mbit – 1 Gbit | 64–128 KB | niska RTT, krótkie kolce |
| Obciążenie mieszane: strona internetowa + pliki do pobrania | 1–10 Gbit | 128–256 KB | Wyrównanie wyłączone Przepustowość oraz opóźnienie |
| Replikacja/kopie zapasowe | 1–10 Gbit | 256–512 KB | stały przepływ objętościowy, akceptowalny Opóźnienie |
| Sieć WAN o wysokim RTT | 10–100 Mbit | 96–192 KB | krótsze serie, bardziej sprawiedliwe Wskazówki |
Współdziałanie QDisc i kontroli przeciążenia
TSQ działa na wejściu do QDisc, podczas gdy algorytmy takie jak fq_codel zarządzają zatłoczeniem na łączu. Razem zmniejszają one kolejki i zapewniają sprawiedliwy podział. Dzięki TCP BBR dodatkowo zyskuję, ponieważ bardziej realistyczne pomiary RTT pozwalają na lepsze rozłożenie obciążenia i sterowanie cwnd. CUBIC również działa płynniej, gdy wyłączam nadmierne czasy kolejkowania. W ten sposób przepustowość rośnie w sposób naturalny, podczas gdy Czas reakcji pozostaje pod kontrolą.
Wirtualizacja i platformy chmurowe
W maszynach wirtualnych nakładają się na siebie różne poziomy buforowania: QDisc gościa, kolejki virtio/vhost, QDisc hosta oraz fizyczna karta sieciowa. Utrzymuję TSQ w stanie aktywnym w systemie gościa i ustalam tam konserwatywny limit, aby do hosta nie docierały duże impulsy danych. Na poziomie hiperwizora dbam o krótkie łańcuchy opóźnień, stosując sprawiedliwe mechanizmy QDisc, umiarkowane rozmiary pierścieni TX oraz prawidłowe przypisanie przerwań IRQ. SR-IOV może zmniejszyć opóźnienia, ale przenosi odpowiedzialność na systemy-goście: bez TSQ w systemie-gości istnieje ryzyko długich kolejek VF. W kontenerach TSQ działa na NetNS jak zwykle; dzięki cgroup-pacingowi i limitom CPU zapobiegam sytuacji, w której hałaśliwy sąsiad pośrednio zwiększa opóźnienie. Ważne jest również zwrócenie uwagi na koalescencję i odciążanie w ścieżce virtio: nadmierne grupowanie wydłuża potwierdzenia (Ack), zbyt małe zmniejsza wydajność – dostosowuję ustawienia zgodnie z docelową opóźnieniem, a nie w sposób dogmatyczny.
Wi-Fi i systemy wbudowane: jak prawidłowo postępować w sytuacjach szczególnych
W przypadku połączeń Wi-Fi liczy się Agregacja w warstwie MAC. Jeśli pozostawię zbyt mało bajtów w ścieżce transmisji, sterownik może zagregować mniej ramek, co obniża wydajność. W takich konfiguracjach ostrożnie zwiększam limit i sprawdzam stopień agregacji. Platformy OpenWrt i wbudowane korzystają ponadto z uproszczonych ścieżek w sterownikach i mniejszej liczby operacji atomowych. Każde dostosowanie testuję pod rzeczywistym obciążeniem sieci bezprzewodowej, zanim Profil rozwałkować na szerokość.
Monitorowanie i wskaźniki, które naprawdę się liczą
Obserwuję RTT— Rozkład na gniazdo i zwracam uwagę na wartości odstające, a nie tylko na średnie. Za pomocą ss, tc i eksporterów odczytuję długości kolejek, liczbę retransmisji oraz pacing_rate. Programy eBPF dostarczają mi zdarzenia, gdy gniazda są ograniczane i znów stają się dostępne. Wskaźnik „Time-to-First-Byte” oraz 95. i 99. percentyl pokazują, czy TSQ działa skutecznie. Bez danych pomiarowych każda Optymalizacja lot na ślepo.
Testy A/B i testy obciążeniowe o wysokiej miarodajności
Mierzę efekty TSQ w sposób powtarzalny: najpierw wartość bazową bez zmian, a następnie izolowane zmiany parametrów (np. 64, 96, 128, 192 KB). W przypadku obciążeń mieszanych uruchamiam równoległe strumienie (masowe + wiele krótkich żądań) i porównuję 95. oraz 99. percentyl opóźnień, a nie tylko medianę. Nawet jeśli wyraźnie przerywam przebiegi testowe (rozgrzewka, okno pomiarowe, wyhamowanie), artefakty pozostają rozpoznawalne. Zwracam uwagę na stałe parametry: te same wzorce ładunku, identyczną trasę/MTU, identyczne częstotliwości procesorów serwerów i klientów. Na łączach WAN symuluję opóźnienia, jitter i utratę pakietów za pomocą tc netem, aby sprawdzić, czy limity TSQ nie są zbyt wcześnie ograniczane przy wysokim BDP. Dopiero gdy percentyle się zawężają, a liczba retransmisji i utraty pozostają stabilne, wprowadzam te wartości do środowiska produkcyjnego.
Optymalizacja sprzętu i szczegóły dotyczące sterowników
Sprawdzam ustawienia TSO/GSO, bufor pierścieniowy karty sieciowej oraz sterowanie IRQ, aby TSQ działa sprawnie. Zbyt duże pierścienie TX wydłużają kolejkę przy urządzeniu; zbyt małe zmniejszają wykorzystanie mocy obliczeniowej. Zgrubne grupowanie przerwań opóźnia potwierdzenia (ACK), natomiast precyzyjne grupowanie zwiększa obciążenie procesora. Dostosowuję moderację do praktycznych potrzeb i na początek odsyłam do Koalescencja przerwań. Celem pozostaje niezawodna Opóźnienie przy wydajności pozwalającej na utrzymanie eksploatacji.
NUMA, RSS i powinowactwo procesora
Krótkie kolejki nie mają większego sensu, jeśli pakiety nieustannie przemieszczają się poza granice NUMA. Przypisuję kolejki RX/TX za pomocą RSS/irqbalance do rdzeni tej samej domeny NUMA, w której działa aplikacja. Za pomocą XPS/RPS kontroluję, które procesory przejmują obciążenie TX, unikając w ten sposób przeskakiwania między gniazdami. Mniejsza liczba nieudanych odwołań do pamięci podręcznej i mniejsze konflikty blokad pośrednio pomagają TSQ: potwierdzenia zakończenia są zwracane szybciej, gniazdo jest wcześniej „odblokowywane“, a szczyty opóźnień nie występują. W przypadku bardzo dużej liczby przepływów na jeden host planuję wystarczającą liczbę kolejek i unikam kolizji kilku intensywnych przepływów na tym samym pierścieniu TX.
Krok po kroku: Sprawdzanie działania TSQ
Zaczynam od spojrzenia na Sysctl: Polecenie `sysctl net.ipv4.tcp_limit_output_bytes` pokazuje aktualny limit. Następnie sprawdzam za pomocą `ss -tin` poszczególne gniazda, zwracam uwagę na `send‑q` i `rtt` oraz porównuję fazy obciążenia z dostosowanym limitem i bez niego. Za pomocą iperf3 generuję obciążenie w tle i równolegle mierzę czasy odpowiedzi API, aby uwidocznić priorytety. Polecenie tc -s qdisc dostarcza mi dane dotyczące liczby pakietów i odrzuconych pakietów w dyscyplinie wyjściowej. Jeśli 95. i 99. percentyl pozostają w wąskim przedziale, a CPU– Obciążenie w ramie, dostosuj wybór limitu.
Częste błędy i antywzorce
- „Większy bufor = większa wydajność“: To stwierdzenie sprawdza się w testach przepustowości bez docelowego opóźnienia, ale nie ma zastosowania w przypadku usług interaktywnych. TSQ zastępuje nadmiernie rozbudowane kolejki limitami dostosowanymi do zapotrzebowania dla każdego strumienia.
- „TSQ wpływa na przepustowość“: Przy prawidłowych ustawieniach TSQ ogranicza impulsy, a nie średnią szybkość. W przypadku obciążeń masowych umiarkowanie zwiększam limit i mierzę percentyle, a nie tylko szczytową wartość w Mbit/s.
- „Wystarczy samo pacing“: Wygładzanie czasowe jest ważne, ale bez ograniczenia liczby bajtów duże ramki GSO i tak przedostają się do QDisc. TSQ i pacing wzajemnie się uzupełniają.
- „Jedna wartość dla wszystkich“: obciążenia, łącza i karty sieciowe różnią się między sobą. Pracuję z wartościami zakresowymi i sprawdzam poprawność dla każdego środowiska z osobna.
- „Dotyczy tylko protokołu TCP“: skupiamy się na protokole TCP, ale w systemie istnieją inne parametry regulacyjne (np. dotyczące obciążenia protokołu UDP). Zapobiegam niekontrolowanemu zatykaniu tych samych kolejek przez równoległe protokoły.
Podsumowanie: Precyzyjna kontrola opóźnień
TSQ przenosi kontrolę nad kolejkami sterowników do Gniazdo i w ten sposób ograniczam zatory bezpośrednio u źródła. Ograniczam liczbę bajtów buforowanych wstępnie dla każdego strumienia, zapewniając w ten sposób szybkie potwierdzenia (Ack), niższy czas RTT oraz sprawiedliwy podział kolejek. W połączeniu z fq_codel i nowoczesną kontrolą przeciążenia czas reakcji pozostaje niezawodny nawet pod obciążeniem. Szczególne przypadki związane z sieciami Wi-Fi i systemami wbudowanymi rozwiązuję za pomocą dostosowanych limitów i testów w rzeczywistych warunkach. Kto obserwuje wskaźniki i stopniowo dostosowuje limity, ten utrzymuje Opóźnienie stały na niskim poziomie, bez niepotrzebnej utraty przepustowości.


