Jak przygotować infrastrukturę pod AI, by uniknąć wąskich gardeł wydajności

0
25
Rate this post

Nawigacja:

Od czego zacząć: czego naprawdę potrzebuje Twoje AI

Różnica między „AI jako eksperyment” a „AI w produkcji”

Infrastruktura pod AI wygląda zupełnie inaczej, gdy kilka osób testuje modele na potrzeby PoC, a inaczej, gdy te same modele obsługują tysiące zapytań na minutę w systemie krytycznym. Mieszanie tych dwóch perspektyw prowadzi prosto do przepalonych budżetów i wąskich gardeł wydajności.

AI jako eksperyment to sytuacja, w której:

  • modele są często zmieniane,
  • zestawy danych rosną, ale nadal mieszczą się na pojedynczych maszynach lub w prostym klastrze,
  • ważniejsza jest elastyczność i szybkość „od pomysłu do wyniku” niż absolutna stabilność czy SLA,
  • część zadań można spokojnie wrzucać w kolejkę i wykonać nocą.

AI w produkcji wymusza inne priorytety:

  • wymagane są jasno zdefiniowane SLA (czas odpowiedzi, dostępność),
  • pipeline danych i trenowania musi być powtarzalny i automatyzowalny,
  • infrastruktura musi wytrzymać awarie pojedynczych komponentów bez zatrzymania całego systemu,
  • koszt jednostkowy inferencji zaczyna być istotny (szczególnie przy dużym wolumenie zapytań).

Jeśli zespół nie zacznie od uściślenia, czy inwestuje w środowisko eksperymentalne, produkcyjne, czy hybrydę (częste w średnich firmach), to dobór CPU, GPU, storage i sieci będzie zbiorem przypadkowych decyzji. A przypadki w infrastrukturze AI kończą się zwykle tym, że GPU za setki tysięcy złotych stoją bezczynnie, bo blokuje je pojedynczy serwer NFS lub łącze 1 GbE.

Typy obciążeń AI a wymagania infrastruktury

Większość projektów AI da się rozbić na kilka podstawowych typów obciążeń. Każdy z nich ma inny „profil apetytu” na CPU, GPU, RAM, sieć i storage. Uproszczenie wszystkich do „potrzebujemy kilku mocnych GPU” jest prostym przepisem na wąskie gardła wydajności.

Najczęstsze obciążenia:

  • Eksploracja danych (EDA, feature engineering) – dominuje CPU, RAM i I/O do storage. Tu wąskim gardłem staje się zwykle pojedynczy dysk lub ograniczony RAM, nie brak GPU.
  • Trening modeli:
    • dla klasycznych modeli ML (XGBoost, Random Forest, klasyfikatory liniowe) – mocny CPU i dużo RAM będą ważniejsze niż GPU,
    • dla NLP, wizji komputerowej, dużych sieci neuronowych – GPU staje się kluczowe, ale tylko jeśli dane „nadążają” za akceleratorami.
  • Inferencja (wnioskowanie z gotowych modeli):
    • batch (hurtowe przetwarzanie) – zwykle można zredukować wymagania sieciowe i skupić się na przepustowości GPU/CPU,
    • real-time (API online) – czas reakcji zależy od sieci, obciążenia CPU, planisty kontenerów i opóźnień do baz danych.
  • Pipeline ETL/ELT dla danych treningowych – to osobny świat: narzędzia typu Spark/Flink, masowe I/O do hurtowni danych, integracja z feature store. AI często jest tylko ostatnim etapem.

Dla różnych typów zadań nawet ten sam model będzie mieć inne wymagania. Klasyczny przykład: model rekomendacyjny. Trening może wymagać klastra GPU i szybkiego rozproszonego storage, a inferencja – dużej pamięci RAM i szybkich połączeń z bazą user events, ale już niekoniecznie dziesiątek GPU.

Scenariusze zastosowań a profil zasobów

Poszczególne typy zastosowań AI narzucają charakterystyczne profile obciążeń:

  • Klasyfikacja i regresja na tabelach – dominują:
    • CPU (trening i inferencja),
    • RAM (duże macierze, encodery kategorii),
    • storage sekwencyjny (pliki Parquet/CSV).

    GPU przydaje się rzadziej, chyba że pracujemy z ogromnymi tabelami lub wykorzystujemy GPU-owe biblioteki (RAPIDS, cuML).

  • NLP (szczególnie duże modele językowe):
    • VRAM GPU jako główne ograniczenie (pojemność modelu i batch size),
    • przepustowość pamięci GPU i interconnect (NVLink/InfiniBand) przy treningu rozproszonym,
    • sieć przy serwowaniu modeli jako API (przepustowość i opóźnienia).
  • Wizja komputerowa (detekcja, segmentacja, klasyfikacja obrazów):
    • GPU dla treningu,
    • storage – wiele małych plików, często losowy dostęp, rosnące wymagania na IOPS,
    • CPU dla augmentacji i przetwarzania obrazu (czasem węższe gardło niż GPU).
  • Systemy rekomendacyjne:
    • trening – duże macierze, embeddingi, często potrzeba wielu GPU lub mocnych CPU,
    • inferencja – wymagania sieciowe (zapytania online) + szybki dostęp do cech użytkownika (bazy NoSQL, cache in-memory).

Bez rozpoznania, do której z powyższych kategorii należy główny use case, trudno racjonalnie dobrać proporcje CPU, GPU, RAM, storage i sieci. Profil obciążenia powinien być pierwszym dokumentem, zanim ktokolwiek zacznie porównywać specyfikacje kart GPU.

Ocena dojrzałości organizacji i stopnia skomplikowania środowiska

Rozważając infrastrukturę pod AI, trzeba skonfrontować ambicje z realną dojrzałością operacyjną zespołu:

  • Pojedynczy serwer – wystarczający dla małego zespołu badawczo-rozwojowego, PoC i wczesnych eksperymentów. Ograniczenia:
    • brak prawdziwej wysokiej dostępności,
    • trudna współdzielona praca (kolejki na GPU, ręczne zarządzanie projektami),
    • skalowanie wymaga wymiany serwera, a nie prostego dołożenia kolejnych.
  • Mały klaster on-premise – kilka serwerów GPU/CPU, współdzielony storage:
    • wymaga podstawowego poziomu automatyzacji (provisioning, monitoring),
    • wprowadza potrzebę planisty zasobów (Slurm, Kubernetes),
    • daje możliwość rozdzielenia środowisk: eksperymenty, staging, produkcja.
  • Integracja z chmurą – elastyczna skala, ale także:
    • złożone modele kosztowe (płatność za GPU, storage, sieć, transfer danych),
    • konieczność rozsądnej polityki danych (co na miejscu, co w chmurze),
    • potencjalne wąskie gardła na łączu internetowym przy hybrydzie.

Rozwiązaniem nie jest zawsze „idź w chmurę” ani „zbuduj własny klaster GPU”. Rozsądna ścieżka często wygląda tak: pojedynczy mocny serwer + wybrane GPU w chmurze do treningów szczytowych, a dopiero później, przy rosnącym obciążeniu, przejście na stały klaster lub większe rezerwacje zasobów w chmurze.

Dlaczego bez profilowania obciążeń planowanie infrastruktury to strzał w ciemno

Najgorszy scenariusz: organizacja kupuje drogi sprzęt „pod AI”, nie mierząc wcześniej, gdzie faktycznie pojawiają się wąskie gardła wydajności. Rezultat? GPU czeka na dane z wolnego storage, CPU spędza czas na synchronicznym czekaniu na odpowiedzi bazy, a użytkownicy obwiniają „słabą kartę graficzną” albo „framework”.

Profilowanie obciążeń powinno obejmować co najmniej:

  • czas przygotowania batcha danych (CPU, RAM, I/O),
  • czas samego kroku treningowego lub inferencji na GPU/CPU,
  • czas operacji I/O do storage i zewnętrznych usług (bazy, message queue),
  • zużycie zasobów (GPU utilization, CPU usage, IOPS, throughput sieci).

Bez takich danych wybór pomiędzy „dorzucić GPU” a „przebudować storage” jest zgadywaniem. W wielu projektach szybszy storage lub reorganizacja danych przynosi większy zysk niż wymiana kart graficznych.

Krótki przykład: mocne GPU zablokowane przez powolny storage

Typowy scenariusz z praktyki: firma inwestuje w serwer z kilkoma wysokiej klasy GPU, zakładając, że to rozwiąże problem długiego trenowania sieci konwolucyjnych na obrazach. Serwer zostaje podłączony do istniejącego środowiska – współdzielony NFS z macierzą dyskową, która od lat obsługuje również backupy.

Po kilku tygodniach okazuje się, że GPU przez większość czasu mają obciążenie 20–30%. Analiza metryk pokazuje, że:

  • czas wczytywania i augmentacji batchy zdjęć zajmuje więcej niż sam krok treningowy,
  • serwer NFS nie wyrabia z liczbą losowych odczytów małych plików,
  • w godzinach wykonywania backupów wydajność drastycznie spada.

W efekcie potencjał GPU jest marnowany. Realnym rozwiązaniem nie jest kupno kolejnych kart, tylko:

  • przeniesienie danych treningowych na lokalne NVMe,
  • zgrupowanie obrazów w większe pliki (np. TFRecord),
  • dostosowanie pipeline’u augmentacji tak, aby lepiej wykorzystywać CPU i cache.

Taki przypadek dobrze pokazuje, że źródłem wąskiego gardła w systemach AI bardzo często bywa storage i organizacja danych, a nie same akceleratory.

Kluczowe wąskie gardła w systemach AI – mapa ryzyka

Siedem głównych punktów krytycznych infrastruktury pod AI

Przy planowaniu infrastruktury pod AI wygodnie jest myśleć o niej jak o łańcuchu: najsłabszy ogniwo determinuje ogólną wydajność. W obciążeniach AI takie „ogniwa” można z grubsza pogrupować w siedem obszarów.

CPU i planowanie wątków

CPU jest odpowiedzialny za:

  • dekodowanie i wstępne przetwarzanie danych (np. obrazów, tekstu),
  • obsługę frameworków (PyTorch, TensorFlow) i ich wątków roboczych,
  • koordynację pracy GPU,
  • komunikację z systemem plików, bazami, kolejkami wiadomości.

Jeśli CPU jest zbyt słaby lub źle skonfigurowany (np. wątki nie są przypięte do rdzeni, ignorowana jest topologia NUMA), to GPU przez znaczną część czasu nic nie robi, czekając na dane. Objawia się to niskim wykorzystaniem GPU i wysokim obciążeniem pojedynczych rdzeni CPU.

GPU: liczba, pamięć i topologia połączeń

GPU stał się symbolem „infrastruktury AI”, ale sam w sobie też bywa źródłem wąskich gardeł:

  • niewystarczająca pamięć VRAM wymusza małe batch size lub złożone techniki rozdzielania modelu,
  • słaba komunikacja między GPU (brak NVLink/InfiniBand) ogranicza skalowanie treningu rozproszonego,
  • niewłaściwy dobór GPU do zadań inferencyjnych prowadzi do przepłacania za FLOPS, z których model i tak nie korzysta.

Dobór liczby i typu GPU bez analizy charakterystyki modelu (rozmiar, typ operacji, docelowy batch size) i topologii połączeń kończy się często underprovisioningiem (model nie mieści się w VRAM) albo overprovisioningiem (piękne, ale niewykorzystane akceleratory).

RAM i pamięć GPU

Brak RAM lub VRAM nie zawsze objawia się od razu spektakularnym błędem. Częściej przybiera formę „cichej” degradacji wydajności:

  • page faults – częste odwołania do stron, które nie mieszczą się w cache procesora,
  • swap – system operacyjny zaczyna agresywnie przenosić dane z RAM na disk,
  • w GPU – fallback do wolniejszej pamięci hosta (PCIe) lub fragmentacja VRAM utrudniająca alokację dużych tensorów.

W praktyce użytkownik widzi spadające tempo treningu, rosnące opóźnienia inferencji i okresowe zatrzymania, które trudno powiązać z jednym, oczywistym błędem. Monitoryng RAM/VRAM jest krytycznym elementem wczesnego wykrywania takich problemów.

Storage: IOPS, przepustowość sekwencyjna i opóźnienia

Storage jest jednym z najbardziej niedoszacowanych elementów infrastruktury pod AI. Typowe problemy:

  • duża liczba małych plików (obrazy, teksty) skutkuje wysokim obciążeniem operacjami metadanych,
  • współdzielony NFS lub Ceph bez odpowiedniej konfiguracji dla obciążeń AI generuje wysokie opóźnienia losowych odczytów,
  • brak lokalnego cache na węzłach GPU zmusza każdy batch danych do przechodzenia przez sieć.

Wiele zespołów skupia się na teoretycznej przepustowości (MB/s), ignorując IOPS i latency. Przy obciążeniach z wieloma małymi plikami to właśnie IOPS i operacje metadata stają się dominującym ograniczeniem.

Sieć i ruch w obrębie data center

Sieć i ruch w obrębie data center

Sieć rzadko jest pierwszym podejrzanym, gdy model „idzie wolno”. Tymczasem w treningu rozproszonym i przy dużych zbiorach danych to właśnie ona często ogranicza skalowanie horyzontalne.

Przy obciążeniach AI kluczowe są trzy elementy:

  • przepustowość – ile danych można przesłać w jednostce czasu (Gb/s),
  • opóźnienia – ile trwa pojedynczy „round trip” pakietu,
  • jitter – zmienność opóźnień, która potrafi rozsynchronizować węzły.

W praktyce problemy ujawniają się, gdy:

  • trening rozproszony korzysta z wielu GPU na różnych serwerach, a synchronizacja gradientów leci po zatkanym 10 GbE,
  • pipeline inferencyjny musi przy każdym zapytaniu pobierać cechy użytkownika z oddalonego klastra bazy,
  • współdzielony system plików (NFS, Ceph, Lustre) wykorzystuje tę samą sieć, która obsługuje resztę ruchu produkcyjnego.

Nowym standardem w środowiskach GPU staje się 25/40/100 GbE lub InfiniBand, ale sam upgrade „z 10 na 100 GbE” nie rozwiązuje problemu, jeśli:

  • przełączniki są źle skonfigurowane (brak QoS, kolejki, priorytetyzacja ruchu RDMA),
  • brak jest segmentacji sieci (np. osobna sieć dla storage, osobna dla ruchu kontrolnego i użytkowego),
  • rutery brzegowe i firewalle stają się nowymi punktami przeciążenia.

Komunikacja między węzłami treningowymi

Przy treningu rozproszonym głównym źródłem problemów nie jest „sieć w ogóle”, tylko konkretny wzorzec komunikacji między węzłami. Dwa najczęstsze scenariusze:

  • data parallelism – każdy węzeł trenuje kopię modelu na innym fragmencie danych; co krok wymagane jest zebranie i zredukowanie gradientów (all-reduce),
  • model parallelism – model jest podzielony na części między różne GPU/maszyny; co warstwę wymagane jest przesłanie aktywacji.

W pierwszym przypadku przepustowość i opóźnienia determinują, jak szybko można robić kolejne kroki treningu. W drugim – sieć staje się „przedłużeniem magistrali pamięci” i praktycznie każdy mikrobatch „dotyka” sieci. Źle dobrana topologia (GPU łączone wyłącznie przez PCIe + 10 GbE) potrafi zabić skalowanie niemal całkowicie.

Przy większych klastrach GPU nie da się już ignorować:

  • RDMA (InfiniBand, RoCE) – komunikacja z pominięciem stosu TCP/IP,
  • dedykowanych bibliotek do kolektywnej komunikacji (NCCL, Gloo, MPI),
  • topologii sieci (fat-tree, dragonfly) i oversubscription na poziomie przełączników.

Częsty błąd: założenie, że „jak dokupimy kolejne serwery GPU, to trening przyspieszy liniowo”. Jeżeli komunikacja all-reduce zaczyna dominować w czasie kroku treningowego, dodawanie węzłów generuje wzrost narzutu sieciowego, a nie większą przepustowość.

Warstwa aplikacyjna i orkiestracja

Infrastruktura to nie tylko sprzęt; istotną rolę odgrywa sposób uruchamiania i izolacji zadań. Źródłem wąskich gardeł stają się tu:

  • narzut konteneryzacji i wirtualizacji (szczególnie przy braku optymalizacji dla GPU),
  • planer zadań (Slurm, Kubernetes), który źle pakuje workloady na węzły,
  • serwisy pomocnicze – logowanie, monitoring, serwisowanie modeli, rejestr obrazów.

Prosty przykład: inferencja modelu LLM uruchomiona w wielu replikach na Kubernetesie. Jeżeli:

  • każdy pod ma przydzielony mały fragment GPU (np. przez nvidia.com/gpu: 1/4),
  • węzeł ma ograniczony RAM i słabe parametry I/O,
  • horyzontalne autoskalowanie bazuje wyłącznie na CPU usage lub liczbie requestów,

to Kubernetes łatwo doprowadzi do sytuacji, w której GPU są pofragmentowane i niedowykorzystane, a jednocześnie rośnie latencja przez thrashing pamięci i przeciążenie storage.

Warstwa danych: bazy, kolejki, cache

Nawet idealnie dobrany klaster GPU nie pomoże, jeśli dane docierają do modeli przez „szyjkę od butelki” w postaci bazy lub kolejki. Główne punkty bólu:

  • monolityczne bazy relacyjne, które muszą obsłużyć zapytania online do cech użytkownika,
  • zbyt wolne lub źle dobrane indeksy (brak indeksów dla najczęstszych zapytań feature store),
  • kolejki i systemy eventowe (Kafka, RabbitMQ) jako jedyne źródło prawdy dla strumieni danych.

W obciążeniach AI up-front trzeba zdecydować:

  • które dane można trzymać w cache in-memory (Redis, Memcached, wektorowe cache na GPU/CPU),
  • które muszą być trwale przechowywane i w jakiej formie (kolumnowe, time-series, obiektowe),
  • jaką latencję i jaką dostępność trzeba zagwarantować (SLA, SLO, SLI).

Jeśli feature store siedzi w bazie, która ma 95. percentyl odpowiedzi na poziomie kilkudziesięciu milisekund, to inferencja „real-time” na poziomie kilku milisekund po prostu się nie wydarzy – niezależnie od tego, jak szybki jest sam model.

Zbliżenie starej maszyny do pisania z tekstem AI ETHICS na kartce
Źródło: Pexels | Autor: Markus Winkler

CPU i RAM – niedoceniany fundament pod obciążenia AI

Dlaczego CPU bywa pierwszym realnym wąskim gardłem

W większości projektów początkowy odruch to: „kupmy GPU, reszta się jakoś ułoży”. Problem w tym, że CPU:

  • przygotowuje dane (dekodowanie, tokenizacja, augmentacja),
  • zarządza pipeline’ami (kolejki batchy, workerzy, wątki I/O),
  • obsługuje serwowanie modeli (HTTP/gRPC, serializacja JSON/protobuf),
  • koordynuje trening rozproszony (proces nadrzędny, synchronizacja).

Jeżeli CPU nie nadąża, GPU wygląda na „za słabe”, mimo że w rzeczywistości czeka na dane. To typowy przypadek, w którym perfy pokazują 100% użycia jednego rdzenia i dużo wolnych zasobów na pozostałych – efekt blokujących wywołań I/O lub słabo zrównoleglonego kodu.

Dobór liczby rdzeni i klas CPU do obciążeń AI

Przy doborze CPU ma znaczenie zarówno liczba rdzeni, jak i ich wydajność jednowątkowa. Z grubsza można wyróżnić trzy kategorie obciążeń:

  • ciężkie przetwarzanie danych (obraz, dźwięk, duże ETL) – dużo rdzeni, dobra przepustowość pamięci,
  • lekka inferencja z dużą liczbą requestów – liczy się przede wszystkim wydajność na wątek i cache procesora,
  • koordynacja dużej liczby zadań (orkiestracja, schedulery, mikroserwisy) – potrzeba wielu rdzeni, ale niekoniecznie „najszybszych na rynku”.

Przy serwerach GPU sensowne minimum to najczęściej procesory klasy serwerowej z kilkunastoma–kilkudziesięcioma rdzeniami na socket, z dużą liczbą linii PCIe i obsługą szybkiego RAM. Oszczędzanie na CPU przy drogich GPU zwykle kończy się nieproporcjonalną stratą wydajności całego systemu.

NUMA, topologia i przypinanie wątków

W nowoczesnych serwerach z wieloma socketami CPU pojawia się problem NUMA (Non-Uniform Memory Access). Koszt dostępu do pamięci lokalnej dla danego procesora jest niższy niż do pamięci „zdalnej” (przywiązanej do innego socketu). Z punktu widzenia AI oznacza to, że:

  • proces trenujący model powinien być przypięty do rdzeni blisko GPU, z których korzysta,
  • bufory danych wykorzystywane przez dany GPU powinny trafiać do jego „lokalnej” pamięci NUMA,
  • niekontrolowane migrowanie procesów między socketami generuje losowe skoki opóźnień.

Przy mocnych serwerach z wieloma GPU brak zarządzania NUMA prawie zawsze prowadzi do straty kilku–kilkunastu procent wydajności. Nie jest to spektakularna awaria, tylko stały, trudny do wyśledzenia „podatek”.

RAM: ile naprawdę jest potrzebne i kiedy „więcej nie pomaga”

RAM bywa traktowany jak coś, co „po prostu musi być dużo”. W praktyce sensowne pytanie brzmi: ile danych musi być jednocześnie w pamięci w najgorszym przypadku? Na to wpływa m.in.:

  • wielkość batcha treningowego i liczba workerów wczytujących dane,
  • cache na poziomie frameworka (prefetching, pinned memory dla transferu na GPU),
  • inne procesy na serwerze (bazy, cache, sidecary, monitoring).

Jeżeli RAM się „przelewa”, a system zaczyna aktywnie korzystać ze swapu, symptomy są dość charakterystyczne:

  • skokowe wydłużanie kroków treningowych,
  • okresowe „zawieszki” inferencji,
  • rosnące opóźnienia odczytu danych z dysku (page cache zostaje wypchnięty).

Przekroczenie pewnego progu (zwykle >1.5–2x realnej potrzeby) przestaje cokolwiek zmieniać. Z punktu widzenia opłacalności lepiej wtedy zainwestować w szybszy storage lub dodatkowy węzeł niż w kolejny „nadmiarowy” bank RAM.

Zarządzanie pamięcią i uniknięcie silent degradation

Żeby uniknąć cichej degradacji wydajności, przydatnych jest kilka praktyk:

  • ustalenie twardych limitów pamięci na kontenery/procesy (cgroups, limit resources w Kubernetesie) z marginesem bezpieczeństwa,
  • monitorowanie page faults, cache hit ratio i wykorzystania swapu,
  • ścisłe kontrolowanie liczby równoległych workerów I/O i procesów augmentacji.

Przykład z życia: zespół dorzuca kolejne procesy augmentacji zdjęć, licząc na liniowy wzrost throughputu. Do pewnego momentu przyspieszenie rzeczywiście jest, później jednak dalsze zwiększanie workerów zaczyna wypychać page cache, rośnie wykorzystanie RAM, system sięga po swap i cały pipeline zwalnia – mimo „większego równoleglenia”.

GPU i akceleratory – jak dobrać, by nie przepłacić i nie „przydusić” modeli

Dobieranie GPU do typu obciążenia, a nie odwrotnie

Rynek kart do AI jest już na tyle rozbudowany, że „kupmy najmocniejsze, jakie są” przestaje być racjonalną strategią. Kluczowe jest pytanie: dominujący use case to trening czy inferencja, a jeśli inferencja – o jakiej skali i SLA?

Można wyróżnić trzy główne kategorie scenariuszy:

  • trening dużych modeli (NLP, CV, LLM) – priorytetem jest duża pamięć VRAM, wysoka przepustowość pamięci, szybka komunikacja między GPU (NVLink/InfiniBand),
  • trening średnich i małych modeli – ważna jest cena za FLOPS, liczba kart w jednym serwerze i dostępność w chmurze,
  • inferencja na produkcji – liczy się cena za zapytanie, pobór mocy, obsługa mniejszych batchy i latencja.

Do inferencji skaluje się często lepiej wiele tańszych GPU (lub nawet CPU z akceleracją wektorową) niż kilka topowych kart treningowych. Dla treningu ogromnych modeli jest odwrotnie.

Pamięć VRAM: realne minimum i wpływ batch size

To, czy model „się zmieści”, to tylko część historii. Kluczowe jest, jak duży batch można utrzymać w pamięci przy założonej architekturze i precyzji obliczeń (fp32, fp16, bf16). Zbyt mały batch oznacza:

  • mniejszą efektywność wykorzystania GPU (więcej narzutu na krok treningowy),
  • często gorszą stabilność uczenia i potrzebę dostrajania hiperparametrów,
  • niższą przepustowość przy inferencji (mniej requestów przetwarzanych równolegle).

Częstą pułapką jest opieranie decyzji na „tabliczkach VRAM” i przykładach z internetu. Dwie implementacje tego samego modelu potrafią mieć różne profile pamięciowe (inne buforowanie, inna sekwencja operacji, różne tricki optymalizacyjne). Zanim zostanie podjęta decyzja zakupowa, rozsądniej jest:

  • zbudować realistyczny prototyp modelu i pipeline’u danych,
  • sprawdzić wykorzystanie VRAM dla kilku kombinacji batch size i precyzji,
  • uwzględnić margines na rozrost modelu i dodatkowe funkcje (np. explainability, logging na poziomie tensorów).

Precyzja obliczeń i wsparcie sprzętowe

Nowe generacje GPU oferują specjalizowane jednostki (Tensor Cores, Matrix Cores) zoptymalizowane pod niższe precyzje (fp16, bf16, int8). Teoretyczne przyspieszenia wyglądają imponująco, ale w praktyce zależą od:

Jak realnie wykorzystać niższe precyzje

Teoretyczny „x-krotny” zysk z fp16/bf16 czy int8 pojawia się tylko wtedy, gdy cały krytyczny fragment obliczeń faktycznie używa tych formatów. W praktyce ograniczenia narzucają:

  • obsługiwane operacje – nie wszystkie warstwy i funkcje aktywacji są równie dobrze zoptymalizowane w niższych precyzjach,
  • stabilność uczenia – niektóre modele (szczególnie RNN-y, stare architektury) są bardziej wrażliwe na utratę precyzji niż nowoczesne transformatory,
  • tooling – automatyczny mixed precision różni się między frameworkami (PyTorch, TensorFlow), a szczegóły konfiguracji potrafią zmienić wynik o kilkadziesiąt procent.

Najczęstszy scenariusz to mixed precision: krytyczne operacje (macierzowe, convolutions) w fp16/bf16, a wrażliwe na numerykę elementy (np. sumowania gradientów, warstwy normalizujące) w fp32. Przy inferencji coraz częściej wchodzi w grę także int8, ale:

  • dla modeli „wrażliwych” (np. generatywne LLM-y) wymaga to często starannej kalibracji i czasem „per-warstwowej” decyzji, co kwantyzować,
  • zysk z int8 jest z reguły większy w mniejszych batchach, gdzie punkt ciężkości przesuwa się na opóźnienie, a nie czysty throughput.

Zanim infrastruktura zostanie zaprojektowana pod konkretny format, lepiej przeprowadzić serię eksperymentów z rzeczywistymi modelami i danymi, zamiast zakładać, że oficjalne benchmarki przeniosą się 1:1.

Topologia GPU: PCIe vs NVLink i kiedy ma to znaczenie

W prostych scenariuszach (pojedynczy GPU, lekka inferencja) temat sposobu połączenia kart jest prawie niewidoczny. Przy:

  • treningu rozproszonym typu data parallel z dużymi batchami,
  • model parallel / pipeline parallel dla dużych modeli,
  • intensywnym wykorzystaniu tensor/gradient sharding,

różnica między komunikacją po samym PCIe a NVLink/InfiniBand potrafi zdecydować, czy skalowanie na 4–8 kartach ma sens.

Typowy schemat degradacji wygląda tak:

  • na pojedynczym GPU step time jest akceptowalny,
  • na 2 GPU czas się poprawia niemal liniowo,
  • na 4 GPU przyspieszenie zwalnia, a potem praktycznie się zatrzymuje – koszt synchronizacji dominuje nad czasem obliczeń.

Jeżeli węzły GPU mają ograniczoną przepustowość PCIe, a nie ma NVLink ani szybkiej sieci między węzłami, to dodawanie kolejnych kart głównie „grzeje powietrze”. Na etapie projektowania serwerów opłaca się sprawdzić:

  • jak są rozmieszczone sloty PCIe względem socketów CPU (NUMA + PCIe topology),
  • czy karty są spięte NVLinkiem, a jeśli tak – w jakiej konfiguracji (pełna siatka, ring, częściowe połączenia),
  • jaką minimalną przepustowość komunikacji zakładają używane frameworki rozproszone (Horovod, DeepSpeed, DistributedDataParallel).

Oversizing GPU: kiedy karta jest zbyt mocna jak na resztę systemu

Dość częsta sytuacja: topowe GPU, a obok nich:

  • powolny storage, który nie jest w stanie dostarczyć danych,
  • CPU z małą liczbą rdzeni lub słabą pamięcią,
  • sieć 1/10 GbE przy rozproszonym treningu.

Efekt jest prosty – analiza metryk użycia pokazuje kilkadziesiąt procent zajętości GPU i „sensację”, że „model jest słabo zoptymalizowany”. Tymczasem realny bottleneck leży poza kartą. W takim scenariuszu lepszym wyborem bywa:

  • tańsze GPU,
  • mocniejsze CPU, szybszy storage i/lub lepsza sieć,
  • rozsądne ograniczenie rozmiaru batchy, ale pełne dociążenie kart.

Dobrym nawykiem jest liczenie kosztu za efektywnie wykorzystany FLOP, a nie za „naklejkę na karcie”. Jeśli profilowanie pokazuje, że GPU są zajęte przez 40–50% czasu, to budżet lepiej przesunąć w stronę komponentów, które ten czas marnują.

Akceleratory wyspecjalizowane: kiedy mają sens

Poza klasycznymi GPU pojawia się coraz więcej wyspecjalizowanych akceleratorów (TPU, IPU, NPU, karty inferencyjne). Wspólny mianownik:

  • świetna efektywność energetyczna i wysoka przepustowość przy konkretnych typach modeli,
  • silne przywiązanie do konkretnego ekosystemu oprogramowania.

One rzadko są dobrym „pierwszym wyborem” przy młodych projektach, które intensywnie eksperymentują z architekturami. Zyskują sens, gdy:

  • architektury modeli się ustabilizowały,
  • profil obciążeń jest przewidywalny (np. duże, stałe wolumeny inferencji LLM),
  • zespół akceptuje koszt wejścia w nowy toolchain i ograniczenia kompatybilności.

Jeśli firma dopiero szuka „product–market fit” dla rozwiązań AI, elastyczność klasycznych GPU (i nawet dobrze dobranych CPU) bywa warta więcej niż potencjalne oszczędności energetyczne z niszowego akceleratora.

Stara maszyna do pisania na dworze z kartką z napisem AI ethics
Źródło: Pexels | Autor: Markus Winkler

Storage i I/O – dlaczego dyski potrafią zabić wydajność AI

Profil I/O typowych pipeline’ów AI

Wydajność storage’u dla AI nie sprowadza się do „ile ma IOPS-ów”. Pojawiają się co najmniej trzy różne profile I/O:

  • uczenie na dużych zbiorach – dominują sekwencyjne odczyty dużych plików (obrazy, wideo, archiwa) z domieszką losowych skoków,
  • feature store / wektorowe wyszukiwanie – wiele losowych odczytów małych fragmentów danych, silna zależność od latencji,
  • logi, metryki, checkpointy – okresowe, często masywne zapisy, niekiedy zsynchronizowane między wieloma węzłami.

Te obciążenia często lądują na tym samym storage’u „bo tak jest wygodniej”, co kończy się nieprzewidywalnymi skokami opóźnień. Rozdzielenie ścieżek I/O na poziomie warstw (NVMe lokalnie, NAS/obiektówka zdalnie, osobne klastry pod logi) bywa mniej efektowne w slajdach, ale dużo skuteczniejsze w praktyce.

NVMe lokalne vs sieciowy storage

Instynkt podpowiada: „dać wszystko na najszybsze NVMe i mieć spokój”. Problem zaczyna się, gdy:

  • zbiory danych mają dziesiątki terabajtów,
  • modele trenują się na wielu węzłach,
  • te same dane mają być współdzielone między środowiskami (R&D, staging, produkcja).

Lokalne NVMe świetnie nadają się do:

  • cache’u gorących danych treningowych (np. ostatnio używane pliki, shard aktualnej epoki),
  • szybkiego zapisu checkpointów bez obciążania sieci.

Natomiast rolę „źródła prawdy” częściej pełni:

  • obiektówka (S3-kompatybilna) – tania, skalowalna, dobra do przechowywania surowych danych i archiwalnych modeli,
  • rozproszony system plików (Ceph, Lustre, BeeGFS) – tam, gdzie potrzebny jest POSIX-owy interfejs i współdzielenie danych między klastrami.

Rozsądny kompromis to wielopoziomowy cache: mały, bardzo szybki poziom na lokalnych NVMe, wyżej tańsza warstwa sieciowa. Bez tego trening dużych modeli kończy się losowymi spadkami throughputu przy każdym „zimnym” odczycie z obiektówki.

Organizacja danych na dysku: formaty i layout

Na wydajność I/O wpływa nie tylko hardware, lecz także sposób spakowania danych. Klasyczne antywzorce:

  • miliony małych plików (np. pojedyncze obrazy) na systemie plików, który nie radzi sobie z taką liczbą inode’ów,
  • duże, trudno podzielne archiwa, przez które każdy worker musi przebijać się liniowo,
  • brak jakiegokolwiek sharding-u dopasowanego do liczby workerów.

Przy dużej skali bardziej praktyczne bywają:

  • formaty sekwencyjne z wewnętrznym indeksem (TFRecord, WebDataset, Parquet dla danych tabelarycznych),
  • podział zbiorów na „shardy” zbliżone rozmiarem do logicznych jednostek pracy (np. epoka treningu, konkretny worker),
  • prekompresja w sposób zrównoważony – kompresja, która minimalizuje rozmiar, ale zabija CPU przy dekompresji, zwykle gorzej wypada niż „nieidealna”, ale lekka.

Jeżeli I/O od początku jest zaprojektowane pod równoległe odczyty, łatwiej utrzymać stały throughput przy rosnącej liczbie workerów, zamiast odkrywać „ścianę” przy którymś kolejnym GPU.

Checkpointy, snapshoty i ochrona przed „katastrofą w połowie treningu”

Przy długich treningach checkpointy są nieuniknione, ale sposób ich zapisu ma realny wpływ na wydajność:

  • zapisywanie gigantycznego checkpointu na współdzielony storage w jednym momencie potrafi przydusić cały klaster,
  • brak rotacji i kompresji powoduje lawinowy wzrost przestrzeni zajętej na dyskach,
  • brak spójnego mechanizmu „recovery” skutkuje koniecznością startu od zera po awarii.

Przydatne praktyki to m.in.:

  • inkrementalne checkpointy lub snapshoty na poziomie systemu plików (jeśli infrastruktura na to pozwala),
  • kaskadowanie: szybki zapis lokalnie + asynchroniczna replikacja na wolniejszy, ale odporniejszy storage,
  • testy odtwarzania modeli z checkpointów zamiast zakładania, że „skoro zapis się udał, to odczyt się uda”.

Monitoring I/O i sygnały zbliżającego się wąskiego gardła

Storage rzadko „pęka” z dnia na dzień. Zwykle sygnały pojawiają się dużo wcześniej:

  • rosnący czas odpowiedzi na losowe odczyty przy tej samej liczbie requestów,
  • wysokie, długotrwałe wykorzystanie kolejki I/O (queue depth),
  • coraz większe odchylenie standardowe czasów kroków treningowych przy pozornie stałym obciążeniu.

Jeśli system metryk ma dostęp do danych z warstwy storage, korelacja „spike I/O – spadek GPU utilization” zwykle jest łatwa do wychwycenia. Trudniejsza część to decyzja, czy inwestować w szybsze dyski, reorganizację danych, czy zmianę sposobu batchowania i preloadingu.

Sieć i komunikacja między węzłami – cichy zabójca treningu rozproszonego

Kiedy sieć zaczyna mieć znaczenie

Dla pojedynczego serwera z jednym GPU sieć bywa niemal obojętna – liczy się tylko dostęp do źródeł danych i endpointów inferencyjnych. Sytuacja zmienia się diametralnie przy:

  • treningu rozproszonym na wielu węzłach,
  • deploymentach modeli w kilku regionach lub data center,
  • feature store’ach i wektorowych wyszukiwarkach działających przez sieć.

Tu często pojawia się uproszczenie: „10 GbE wystarczy”. Bywa, że tak, ale:

  • dla modeli z dużą liczbą parametrów i intensywną synchronizacją gradientów 10 GbE szybko staje się sufitem,
  • dla inferencji LLM-ów w architekturze mikroserwisowej istotna jest nie tylko przepustowość, ale jitter i latencja między usługami.

Topologie sieciowe i wybór sprzętu

Na poziomie fizycznym dostępne są różne opcje:

  • klasyczny Ethernet (10/25/40/100 GbE) – elastyczny, tani, łatwo dostępny; wystarczający dla wielu zastosowań,
  • InfiniBand – wysoka przepustowość, niskie opóźnienia, wsparcie RDMA; częściej pojawia się w dedykowanych klastrach HPC/AI,
  • RDMA over Converged Ethernet (RoCE) – próba pogodzenia zalet RDMA z istniejącą infrastrukturą Ethernet.

To, co się opłaca, w dużej mierze zależy od:

  • skali klastra (kilka vs kilkadziesiąt–kilkaset węzłów),
  • dominującego typu treningu (data parallel vs model parallel),
  • oprogramowania (czy używane frameworki efektywnie korzystają z RDMA).

Dla mniejszych zespołów z kilkoma węzłami różnica między dobrze skonfigurowanym 25/100 GbE a InfiniBandem bywa mniej znacząca niż różnica między „byle jakim” a solidnie zaprojektowanym Ethernetem (QoS, priorytety, segmentacja ruchu).

Najczęściej zadawane pytania (FAQ)

Od czego zacząć planowanie infrastruktury pod AI: od GPU, chmury czy czegoś innego?

Punkt startowy to nie wybór GPU ani dostawcy chmury, tylko doprecyzowanie, czy budujesz środowisko do eksperymentów, produkcyjne, czy hybrydę. Infrastruktura do PoC może być prosta (jeden serwer, podstawowy storage), podczas gdy środowisko produkcyjne wymusza SLA, automatyzację pipeline’ów i odporność na awarie.

Bez tej decyzji inwestycje szybko zamieniają się w chaos: za mocne GPU stoją bezczynnie, bo blokuje je wolny storage albo łącze 1 GbE, a zespół wini „złą kartę” zamiast złą architekturę. Najpierw określ więc typ obciążeń i priorytety (czas odpowiedzi vs koszt vs elastyczność), dopiero potem dobieraj konkretne komponenty.

Jak odróżnić „AI eksperymentalne” od „AI w produkcji” pod kątem infrastruktury?

Środowisko eksperymentalne ma wspierać szybkie zmiany: częste modyfikacje modeli, różne zbiory danych, brak twardych SLA. Można tolerować kolejki na GPU, ręczne uruchamianie zadań czy nocne batchowe przetwarzanie. Jeden mocny serwer lub prosty klaster często wystarcza.

Środowisko produkcyjne to inna liga. Potrzebne są jasno zdefiniowane SLA (czas odpowiedzi, dostępność), powtarzalne i automatyczne pipeline’y danych i trenowania, monitoring oraz odporność na awarie pojedynczych komponentów. Dochodzi też wymóg kontroli kosztu jednostkowego inferencji, bo małe nieefektywności przy dużym wolumenie zapytań przekładają się na realne kwoty.

Jak dobrać proporcje CPU, GPU, RAM i storage do mojego projektu AI?

Kluczowe jest zrozumienie profilu obciążeń, a nie „średnich potrzeb AI”. Eksploracja danych i klasyczne modele ML zwykle zużywają głównie CPU, RAM i I/O do storage. Duże modele NLP i wizji komputerowej dociążają przede wszystkim GPU, ale tylko wtedy, gdy dane są dostarczane wystarczająco szybko. Inferencja online z kolei często „dusi się” na sieci i dostępie do baz danych, a nie na samym modelu.

Praktyczny schemat jest prosty: określ główny use case (np. NLP, system rekomendacji, klasyfikacja tabel), dla każdego etapu (EDA, trening, inferencja, ETL) zmierz, co jest dziś wąskim gardłem, a dopiero później ustal proporcje CPU/GPU/RAM/storage. Unikaj założenia „GPU rozwiąże wszystko” – w wielu przypadkach lepszy efekt daje szybszy storage lub więcej RAM.

Jak uniknąć wąskich gardeł, gdy korzystam z drogich GPU do trenowania modeli?

Najczęstszy błąd to skupienie się na specyfikacji GPU i kompletnym zignorowaniu ścieżki danych. Drogi akcelerator niewiele pomoże, jeśli batch danych jest przygotowywany długo z powodu wolnego dysku, pojedynczego serwera NFS lub zbyt agresywnej augmentacji obrazów wykonywanej na CPU. Efekt: wykorzystanie GPU na poziomie 20–30% i wrażenie „ten sprzęt jest słaby”.

Żeby tego uniknąć, trzeba mierzyć: czas wczytywania i przygotowywania batchy, obciążenie CPU i IOPS storage, przepustowość sieci. Często lepszym ruchem jest reorganizacja danych (np. łączenie wielu małych plików, cache lokalny na SSD, zmiana formatu) niż dołożenie kolejnej karty graficznej. Reguła: jeśli GPU się nudzi, problem prawie zawsze leży poza GPU.

Czy do AI wystarczy jeden serwer, czy od razu budować klaster lub iść w chmurę?

Dla małego zespołu R&D, PoC i wczesnych eksperymentów często w zupełności wystarcza jeden mocny serwer z dobrze dobranym GPU, dużą ilością RAM i szybkim lokalnym storage. Ograniczenia są jasne: brak wysokiej dostępności, trudniejsza współpraca wielu osób i skalowanie wymagające wymiany sprzętu, a nie prostego dołożenia kolejnych węzłów.

Klastry on‑premise i chmura wchodzą w grę przy rosnącej skali i wymaganiach produkcyjnych. Klaster oznacza konieczność ogarnięcia automatyzacji, monitoringu i planisty (Kubernetes, Slurm), chmura – z kolei złożony model kosztowy, politykę danych i potencjalne wąskie gardła łącza przy scenariuszu hybrydowym. Często sensowną ścieżką jest kompromis: pojedynczy serwer lokalnie + okresowe użycie GPU w chmurze do treningów szczytowych, a dopiero potem decyzja o stałym klastrze lub większych rezerwacjach w chmurze.

Jak profilować obciążenia AI, żeby sensownie zaplanować infrastrukturę?

Profilowanie polega na rozbiciu procesu na etapy i zmierzeniu, co naprawdę trwa najdłużej oraz które zasoby są przeciążone. W praktyce warto mierzyć co najmniej: czas przygotowania batcha danych (CPU, RAM, I/O), czas samego kroku treningowego lub inferencji na GPU/CPU, opóźnienia i przepustowość przy dostępie do storage i zewnętrznych usług, a także wykorzystanie GPU, CPU, dysków i sieci.

Bez takich danych decyzja „kupić więcej GPU czy przebudować storage” jest zgadywaniem. Typowy scenariusz z życia: po włączeniu metryk okazuje się, że głównym problemem jest NFS współdzielony z backupami, a nie „za słaba karta”. Dopiero na tej podstawie można z sensem zdecydować o zmianach w architekturze lub zakupach sprzętu.

Jakie są typowe wąskie gardła wydajności w projektach AI i jak je rozpoznać?

Najczęstsze wąskie gardła to: wolny lub źle zorganizowany storage (dużo małych plików, jeden NFS), niewystarczający RAM przy dużych tabelach, przeciążona sieć przy inferencji online oraz brak planisty zasobów w środowisku współdzielonym. GPU rzadko jest realnym problemem jako pierwsze ogniwo – częściej czeka na dane.

Rozpoznanie jest dość proste, ale wymaga dyscypliny: uruchom monitoring, porównaj czas spędzony na I/O vs czas obliczeń, sprawdź wykorzystanie CPU/GPU/RAM/dysku w trakcie typowego treningu lub inferencji. Jeśli któryś komponent jest permanentnie „na czerwono”, a inne się nudzą, z dużym prawdopodobieństwem to właśnie on jest wąskim gardłem – i tam ma sens inwestycja lub zmiana architektury.

Najważniejsze punkty

  • Pierwsza decyzja to rozróżnienie, czy budujemy środowisko eksperymentalne, produkcyjne, czy hybrydowe – mieszanie tych trybów w jednej, „uniwersalnej” infrastrukturze zwykle kończy się przepalonym budżetem i niewykorzystanymi GPU.
  • Profil obciążenia (eksploracja danych, trening, inferencja batch/real-time, ETL/ELT) ma większe znaczenie niż sama moc GPU – bez jego rozpoznania dobór CPU, GPU, RAM, storage i sieci jest w praktyce losowy.
  • Inne typy zadań wymagają innych zasobów: klasyczne ML „ciągną” CPU i RAM, NLP i duże sieci są ograniczane głównie VRAM i interconnectem GPU, a wizja komputerowa często blokuje się na IOPS i CPU do augmentacji, nie na samych akceleratorach.
  • Ten sam model może mieć zupełnie różne potrzeby w treningu i inferencji – np. rekomendacje: trening potrzebuje klastra GPU i szybkiego, rozproszonego storage, a produkcyjne API głównie szybkiego dostępu do cech użytkownika i sieci o niskich opóźnieniach.
  • Bez jasno zdefiniowanych SLA (czas odpowiedzi, dostępność) oraz powtarzalnego pipeline’u danych i trenowania trudno zbudować sensowne środowisko produkcyjne – kończy się to „ręcznym rzemiosłem” i niestabilnością przy większym wolumenie zapytań.
  • Poziom złożoności infrastruktury musi być dopasowany do dojrzałości operacyjnej zespołu: pojedynczy serwer wystarczy na PoC, mały klaster wymaga już automatyzacji i planisty zasobów, a integracja z chmurą dodaje elastyczność, ale też nowe wąskie gardła (koszty, transfer, łącze).