...

Server IRQ Affinity i wielordzeniowa optymalizacja sieci dla maksymalnej wydajności

Optymalizuję ścieżki sieciowe serwera poprzez Przynależność IRQ i mapują kolejki RX/TX na rdzenie, aby kontrolować opóźnienia, przepustowość i jitter p99. Ci, którzy używają procesorów wielordzeniowych, konsekwentnie organizują przerwania, SoftIRQ, NAPI i NUMA w taki sposób, że przepływy pozostają niezależne od rdzeni, przełączanie kontekstu jest ograniczone, a aplikacja reaguje znacznie szybciej.

Punkty centralne

  • Dystrybucja IRQ określa, które rdzenie obsługują przerwania sprzętowe i zapobiega hotspotom.
  • Bliskość NUMA zmniejsza zdalny dostęp i obniża szczyty opóźnień.
  • SoftIRQ i NAPI kontrolować przetwarzanie wsadowe i zmniejszać obciążenie rdzeni.
  • RPS/RFS utrzymuje przepływy blisko wątków konsumujących.
  • Pomiar i przypinanie sprawia, że wydajność jest bardziej deterministyczna.

Dlaczego powinowactwo IRQ ma znaczenie w działaniu serwera?

Wysokie szybkości pakietów szybko obciążają poszczególne rdzenie, jeśli wszystkie przerwania trafiają na kilka procesorów, więc rozkładam obciążenie selektywnie, aby Hotspoty aby tego uniknąć. Przypisuję kolejki RX/TX do odpowiednich rdzeni, aby ścieżki danych były krótkie, a pamięci podręczne ciepłe. Zmniejsza to opóźnienia p95/p99, ponieważ unikam niepotrzebnych migracji i utrzymuję etapy przetwarzania na tych samych rdzeniach. Biorę pod uwagę fizyczną bliskość NIC, kanałów pamięci i gniazd CPU, aby ścieżka od pakietu do pracownika aplikacji pozostawała niezmiennie szybka. To pokrewieństwo rdzeni zapewnia wymierną stabilność podczas szczytów ruchu bez konieczności natychmiastowej aktualizacji sprzętu.

Równoważenie IRQ a stałe powinowactwo

Standardowa usługa irqbalance automatycznie dystrybuuje przerwania, ale nie zna logiki mojej aplikacji, celów NUMA i budżetów opóźnień. Wiążę krytyczne sieciowe IRQ z wybranymi rdzeniami, podczas gdy hałaśliwe lub mniej ważne przerwania są przenoszone do innych rdzeni. To powiązanie harmonizuje z przypinaniem procesów aplikacji, dzięki czemu potok na przepływ pozostaje spójny. Przy dużym natężeniu ruchu unikam redystrybucji, które generują dodatkowy narzut i osłabiają efekt pamięci podręcznej. Jeśli chcesz zagłębić się w temat, możesz znaleźć praktyczne informacje w tym przewodniku: Równoważenie IRQ w centrum danych.

Pokrewieństwo procesorów, NUMA i krótka ścieżka danych

Preferuję przypinanie pracowników aplikacji i sieciowych IRQ do tego samego NUMA-aby dostęp do pamięci pozostał lokalny. Jeśli karta NIC zawiesza się na węźle 0, ustawiam tam również powiązane kolejki RX i wiążę odpowiednie procesy z tymi rdzeniami. W ten sposób unikam kosztownych zdalnych dostępów do pamięci, które mają duży wpływ na opóźnienia przy wysokich prędkościach pakietów. Uwzględniam również pary hiperwątkowości, aby siostrzane wątki nie kolidowały ze sobą. Ten trójkąt przypinania procesów, powinowactwa IRQ i topologii NUMA sprawia, że ścieżki sieciowe są bardziej przewidywalne i zwiększają przepustowość.

Zrozumienie SoftIRQs, NAPI i projektowania kolejek

Po przerwaniu sprzętowym, jądro przejmuje przetwarzanie w SoftIRQs, często na tym samym rdzeniu, który otrzymał IRQ. Gdy obciążenie jest wysokie, świadomie rozkładam obciążenie SoftIRQ, aby złagodzić wąskie gardła bez niepotrzebnej fragmentacji ścieżki danych. Karty sieciowe z wieloma kolejkami pomagają, ponieważ mogę przypisać jasno zdefiniowane rdzenie do każdej kolejki, a tym samym osiągnąć prawdziwą równoległość. Używam NAPI do przetwarzania pakietów w partiach, dzięki czemu nie występują burze przerwań, a czas procesora jest efektywnie wykorzystywany. Ten artykuł zawiera podstawową wiedzę na temat tej ścieżki: SoftIRQ i przepustowość sieci.

RPS/RFS i lokalność przepływu

Używam RPS do szerszej dystrybucji pakietów i używam RFS dzięki czemu przepływy trafiają do zużywających wątków. Dzięki temu dostęp do pamięci podręcznej jest wydajny, a aplikacja korzysta ze stałych czasów odpowiedzi. Harmonizuję strategię hashowania karty sieciowej, liczbę kolejek i zestawy procesorów RPS, aby żadna kolejka jądra nie została przepełniona. Pokrewieństwo przepływów jest szczególnie skuteczne w przypadku wielu krótkich żądań, takich jak te generowane przez interfejsy API i mikrousługi. W ten sposób buduję potok, w którym każdy przepływ dotyka tego samego rdzenia tak często, jak to możliwe, i unikam niepotrzebnych migracji.

RSS, tablica pośrednicząca i XPS: ukierunkowana kontrola haszowania

Aby upewnić się, że dystrybucja rozpoczyna się czysto w NIC, dostosowuję RSS (Receive Side Scaling) i tabelę przekierowań, aby kolejki RX były przypisane dokładnie do rdzeni, które później będą obsługiwać wątki aplikacji. Upewniam się, że liczba kolejek odpowiada liczbie używanych rdzeni i że klucze hash pozostają stabilne, aby przepływy nie wędrowały nieoczekiwanie. Jeśli algorytm hashowania ulegnie zmianie lub tabela przekierowań zostanie dynamicznie nadpisana, spowoduje to utratę lokalności przepływu i przyczyni się do pominięć pamięci podręcznej.

Na ścieżce TX dodatkowo aktywuję XPS (Transmit Packet Steering), aby pakiety wychodzące były wysyłane przez rdzeń, który przetwarza aplikację. Utrzymuje to również pamięć podręczną TX blisko pracownika, a ścieżka od kolejki gniazda do kolejki NIC pozostaje krótka. Utrzymuję spójne mapowania RX i TX, dokumentuję je dla każdego interfejsu i definiuję je w skryptach startowych, aby ponowne uruchomienie nie rozmyło architektury.

Koalescencja przerwań: porównanie opóźnień i przepustowości

Z Koalescencja Podsumowuję przerwania, aby zmniejszyć narzut, ale zwracam uwagę na limity opóźnień mojej aplikacji. W przypadku przesyłania strumieniowego i VoIP zwykle utrzymuję krótkie interwały, podczas gdy transfery masowe dobrze tolerują dłuższe partie. Testuję krok po kroku, mierzę p95/p99 i sprawdzam spadki, retransmisje i wykorzystanie procesora na rdzeń. Dopiero wtedy zapisuję ustawienia i dokumentuję je dla każdego hosta i karty sieciowej. Ten praktyczny artykuł zapewnia głębszy wgląd w ten kompromis: Wyjaśnienie koalescencji przerwań.

Prawidłowe dozowanie ładunków odciążających i agregacja

Ustawiłem GRO/LRO aby zmniejszyć obciążenie procesora, ale sprawdź, czy moje obciążenia korzystają z większych partii. Interfejsy API wrażliwe na opóźnienia często reagują lepiej, gdy GRO jest umiarkowane, a LRO wyłączone, ponieważ duże super pakiety mogą nasilać efekty blokowania head-of-line. W przypadku transferów masowych, replikacji lub kopii zapasowych, używam GRO/GSO/TSO bardziej agresywnie, o ile strona odbiorcy pozostaje stabilna, a wykorzystanie procesora spada.

Odciążanie sumy kontrolnej oraz OSP/GSO Znacznie zmniejszam obciążenie CPU, ale upewniam się, że skrzynki pośredniczące, tunele lub niezgodności odciążania (np. z niektórymi enkapsulacjami) działają poprawnie. Jeśli wystąpią anomalie, stopniowo zmniejszam poszczególne obciążenia i mierzę wpływ na przepustowość, retransmisje i czas procesora. Celem jest zestaw, który pozostaje stabilny we wszystkich obszarach i przewidywalny w godzinach szczytu.

Izolacja procesora, harmonogram i stany energetyczne

W przypadku twardych budżetów opóźnień izoluję rdzenie dla ścieżek sieciowych i pracowników aplikacji. Z Izolacja procesora i strategii lean housekeeping, zapobiegam przedostawaniu się zadań systemowych, wątków Kthreads lub przerwań timera na „gorące“ rdzenie. Naprawiam również CPU Governor do „wydajności“ i ograniczyć głębokie Stany C, jeśli powoduje to opóźnienia w wybudzaniu. Mam oko na temperatury rdzenia, ponieważ gnicie termiczne może zrujnować wszelkie wykończenia.

Wybór Planowanie zajęć wpływa na przewidywalność. Nadaję priorytet wątkom związanym z siecią, ale nie uruchamiam ich agresywnie i wyłącznie, aby nie konkurowały z ksoftirqd o czas procesora. Regularnie sprawdzam, czy ksoftirqd uruchamia się na poszczególnych rdzeniach - to wyraźny znak, że obciążenie SoftIRQ jest zbyt wysokie lub nieprawidłowo rozłożone.

Odpytywanie zajętości i ścieżki o niskich opóźnieniach

Gdy liczą się mikrosekundy, ustawiam Zajęte odpytywanie w ukierunkowany sposób. Aplikacje mogą definiować okna odpytywania dla wybranych gniazd, aby pobierać pakiety bezpośrednio z budżetów NAPI bez oczekiwania na przerwania. Wybieram krótkie interwały odpytywania, aby uniknąć spalania czasu procesora i ograniczyć tę technikę do gorących ścieżek o stałym ruchu. Równolegle dostosowuję budżety netdev umiarkowanie, aby partie były wystarczająco duże bez głodzenia reszty systemu.

Dyscyplina kolejek sieciowych i ich stymulacja

Skonfigurowałem qdisc na interfejs, aby dopasować obciążenie. Używam nowoczesnych dyscyplin, takich jak fq/fq_codel, aby regulować tempo i długości kolejek w celu wygładzenia wybuchów i uniknięcia bufferbloat. W konfiguracjach z wieloma kolejkami, łączę to z mqprio, dzięki czemu klasy ruchu są konsekwentnie przypisywane do odpowiednich kolejek sprzętowych. Wraz z BQL (Byte Queue Limits) w sterowniku zmniejsza opóźnienia przy pełnym obciążeniu, ponieważ kolejka nie rośnie w niekontrolowany sposób.

Ważna jest interakcja z XPS na ścieżce TX: Mapuję kolejki wysyłania do rdzeni, na których lądują również odpowiadające im przepływy RX. W ten sposób oba kierunki przepływu pozostają blisko CPU i osiągam bardziej stabilne czasy odpowiedzi z dwukierunkowymi protokołami (np. HTTP/2, gRPC).

Praktyczny przepływ pracy w systemie Linux

Zaczynam od nagrywania obciążenia, sprawdzam rozkład CPU w top/htop, patrzę na /proc/interrupts i /proc/softirqs i czytam statystyki ethtool, aby rozpoznać wąskie gardła i przygotować następne. Przepływ pracy-krok. Następnie określam identyfikatory IRQ odpowiednich kolejek NIC i ustawiam odpowiednie maski procesora, które równomiernie zajmują rdzenie i uwzględniają NUMA. Następnie przypinam pracowników aplikacji poprzez taskset lub systemd-CPUAffinity do tych samych rdzeni, które obsługują również powiązane kolejki. Aktywuję RPS/RFS tylko tam, gdzie wzmacnia to lokalność przepływu i utrzymuję spójną konfigurację dla każdego interfejsu. Na koniec ponownie mierzę przepustowość, opóźnienia i jitter przed jednolitym wprowadzeniem zmian na wielu hostach.

Pomiar, unikanie p95/p99 i regresji

Nie polegam na przeczuciach, ale mierzę opóźnienia, wskaźniki błędów i wykorzystanie rdzeni przed i po każdej rundzie dostrajania, tak aby p99 pozostaje stabilny. Śledzę również zmiany kontekstu, wskaźniki migracji i obciążenie według typu SoftIRQ, aby wcześnie zidentyfikować ukryte efekty uboczne. Utrzymuję powtarzalność testów, używam tych samych zestawów danych i stałych wersji, aby wyniki pozostały porównywalne. Odkrywam regresje za pomocą kontroli krzyżowych w warunkach szczytowych i bezczynności oraz przy długich przebiegach wytrzymałościowych. Tylko wtedy, gdy metryki, dzienniki i ślady aplikacji są zgodne, deklaruję konfigurację jako nowy stan bazowy.

Wirtualizacja, kontenery i SR-IOV

W środowiskach zwirtualizowanych zapewniam, że vCPU, Pamięć i vNIC maszyny wirtualnej znajdują się na tym samym węźle NUMA, na którym kończy się powiązana fizyczna karta NIC. Tam, gdzie to możliwe, używam SR-IOV, dzięki czemu ścieżka danych jest krótka, a IRQ mogą być powiązane bezpośrednio z rdzeniami gościa. Podpinam vCPU krytycznych maszyn wirtualnych do dedykowanych rdzeni hosta i upewniam się, że IRQ hosta i gościa nie nakładają się na siebie. W konfiguracjach kontenerowych ustawiam cpusets i „gwarantowane“ klasy QoS, aby kontenery robocze i ich sieciowe IRQ otrzymywały czas procesora w przewidywalny sposób.

Sprawdzam, czy irqbalance powinien mieć przewagę w gościu, czy na hoście - w przeciwnym razie podwójne „automatyczne“ powoduje rozmycie. W virtio ustawiam kilka kolejek i mapuję je czysto na vCPU, aby umożliwić równoległą pracę. Jeśli vhost-net wykorzystuje poszczególne rdzenie hosta, redystrybuuję backendy i utrzymuję wątki vhost NUMA blisko fizycznej karty sieciowej.

Rozwiązywanie problemów: szybkie rozpoznawanie wzorców

  • Rdzenie nasycone, ksoftirqd aktywny: Zbliżenie kolejek RX, sprawdzenie liczby kolejek, dostosowanie RPS/RFS lub nieznaczne zwiększenie koalescencji.
  • Jumpy p99 jitter: Sprawdź dryft NUMA, zweryfikuj C-States/Governor, dostosuj odciążenia i rozmiary GRO krok po kroku.
  • Wiele retransmisji/przerwań: Sprawdź rozmiary pierścieni RX/TX, qdisc i BQL, sprawdź tabelę przekierowań i XPS pod kątem spójności.
  • Nierównomiernie rozłożone przepływy: Zrównoważenie RSS hash i indirection table, rozważenie hot flow pinning, utrzymywanie stabilnego hash seed.
  • Problem tylko z maszyną wirtualną: Umieszczenie backendów vhost/virtio blisko NUMA, ocena SR-IOV, rozdzielenie IRQ między hostem a gościem.

Obejmuje aplikacje i bazy danych

Czysta ścieżka sieciowa jest mało przydatna, jeśli serwery aplikacji lub bazy danych nie działają równolegle, dlatego skonfigurowałem Pracownik-liczba, pule wątków i limity połączeń do dostępnych rdzeni. Przypinam pracowników NGINX lub HAProxy do odpowiednich rdzeni, aby pasowały do kolejek RX. Skaluję PHP-FPM, Node.js, Java lub Go tak, aby faworyzowały lokalną domenę NUMA i w razie potrzeby korzystały z wielu instancji. Integruję pamięci podręczne, takie jak Redis lub Memcached, blisko CPU i zwracam uwagę na ich własne parametry sieciowe i wątkowe. Tylko współdziałanie powinowactwa IRQ, przypinania procesów i skalowania aplikacji zapewnia zauważalny wzrost opóźnień i przepustowości.

Scenariusze hostingu z wysokimi korzyściami

Inwestuję w głębokie dostrajanie głównie wtedy, gdy API generują wiele krótkich żądań lub gdy Czas rzeczywisty-Komunikacja, taka jak VoIP i czaty, wymaga niskich wartości jittera. Konfiguracje e-commerce z obciążeniami szczytowymi są korzystne, ponieważ przepływy kasowe są wrażliwe na opóźnienia. Hosty z wieloma dzierżawcami o dużej gęstości przynoszą korzyści, ponieważ dedykowane rdzenie na kolejkę zmniejszają efekty sąsiedztwa. Usługi przesyłania strumieniowego mogą również osiągnąć większą przepustowość w przeliczeniu na euro bez konieczności natychmiastowego zakupu nowego sprzętu. Koszty pozostają obliczalne, o ile zmiany będą mierzalne i precyzyjnie wprowadzane.

Skrócona tabela referencyjna: rdzenie, kolejki, narzędzia

Korzystam z następującego Tabela jako przypomnienie, gdy konfiguruję nowe hosty lub ponownie kalibruję istniejące konfiguracje. Pokazuje typowe cele, odpowiednie miary, popularne narzędzia Linux i zamierzony wpływ na opóźnienia i przepustowość. Nie używam go dogmatycznie, ale jako punkt wyjścia do serii pomiarów z rzeczywistym ruchem. Jeśli architektura NIC lub topologia NUMA się zmienia, dostosowuję wybór rdzenia. Ważne jest, aby zachować dokumentację dla każdego hosta i zachować możliwość śledzenia zmian.

Cel Pomiar Narzędzie/lokalizacja Linux Oczekiwany efekt
Rozkład obciążenia IRQ Powiązanie wskazówek z rdzeniami /proc/irq/*/smp_affinity Mniej hotspotów, bardziej stałe opóźnienia
Zwiększenie lokalności przepływu Ustawianie zestawów CPU RPS/RFS /sys/class/net/*/queues/*/rps_cpus Mniej migracji, lepsze pamięci podręczne
Kontrola przetwarzania wsadowego Dopracowanie NAPI/koalescencji ethtool -C / domyślne ustawienia sterownika Niższy narzut, kontrolowany jitter
Parowanie aplikacji i IRQ Pracownik pin taskset, systemd CPUAffinity Krótsza ścieżka, niższe p99
Unikaj NUMA Wspólna lokalizacja urządzeń i rdzeni numactl, lscpu, lspci -vv Mniej zdalnego dostępu, większa przepustowość

Najlepsze praktyki, które sprawdzają się w dłuższej perspektywie

Zmieniam tylko jedną dźwignię sterującą na rundę testową, dokumentuję metryki i zapisuję wyniki. Dokumentacja w repozytorium hosta. Utrzymuję spójne konfiguracje, jasno opisując mapowania kolejek do rdzeni i używając skryptów do replikacji. Monitoruję dzienniki pod kątem spadków, retransmisji i limitów czasu i koreluję je z metrykami jądra. W analizie uwzględniam poziom hiperwizora i pamięci masowej, aby nie pozostały żadne wąskie gardła. Mam przygotowane rollbacki na wypadek, gdyby testy wykazały negatywne skutki lub obciążenia uległy zmianie.

Krótkie podsumowanie

Osiągam maksymalną wydajność sieci dzięki wykorzystaniu przerwań, Wskazówki i pracowników, utrzymując w ten sposób stabilną ścieżkę danych na przepływ. IRQ Affinity rozsądnie rozkłada obciążenie sprzętowe, podczas gdy SoftIRQ, NAPI i RPS/RFS sprawiają, że przetwarzanie jest wydajne. Bliskość NUMA chroni przed możliwymi do uniknięcia objazdami pamięci i zmniejsza jitter. Strojenie krok po kroku z powtarzalnymi pomiarami zapobiega błędnym konfiguracjom i pokazuje rzeczywisty postęp. Jeśli pomyślisz o tych elementach razem, możesz śmiało wykorzystać możliwości nowoczesnych serwerów wielordzeniowych dla usług krytycznych pod względem opóźnień.

Artykuły bieżące