Ефективність GPU: чому «краще» не завжди означає «більше»

Було корисно?

Хтось купує блискучий флагманський GPU. Панель живлення загоряється. Рахунок — ще більше. А навчальна задача? Працює… фактично з тією самою швидкістю, що й на старій машині.

Якщо ви коли-небудь дивилися на nvidia-smi, яке показує 20–40% завантаження, і стейкхолдери питають, чому їхнє «оновлення» нічого не оновило — ви в потрібному місці. Жорстока правда: GPU — це не магічний прискорювач; це вибагливий співавтомат з високою потребою в пропускній здатності. Ви їх не «використовуєте». Ви їх годуєте.

Основна ідея: більше не завжди означає краще

«Кращий GPU» зазвичай означає більше обчислювальних ресурсів, більше tensor-ядра, більшу пропускну здатність пам’яті, можливо більше HBM і іноді більше VRAM. Але ваше навантаження — це виробнича лінія, а не один пристрій. Якщо найповільніша станція — CPU для препроцесингу, зчитування зі сховища, PCIe-перекази, all-reduce або накладні витрати на запуск ядер, то великий GPU просто сидить там… ввічливо чекаючи.

У продакшні ефективність GPU — це не «відсоток використання». Це бізнес-пропускна здатність за долар, за ват і за інженер-годину. Великий GPU, що в основному простає, — це не преміальний продукт; це дорогий обігрівач з відмінним маркетингом.

Ось твердження, яке має значення для прийняття рішень:

  • Якщо ви не можете завантажити середній GPU, топовий GPU не виправить ваш конвеєр.
  • Якщо у вас стабільне навантаження, обмежене обчисленнями, великий GPU може бути безумовною перемогою — але лише якщо міжз’єднання, пам’ять і стек ПЗ масштабуються разом з ним.
  • Якщо ви масштабуєтеся на кілька GPU, мережа й операції колективу стають частиною вашого «GPU», подобається вам це чи ні.

Цитата, яку варто приклеїти до монітора: «Надія — не стратегія.» — Gene Kranz

Так, це кліше. Але це те, що я кажу, коли хтось пропонує «просто купимо більші GPU», не вимірявши, куди насправді йде час.

Професійна модель мислення щодо ефективності GPU

1) Думайте про стадії, а не про чипи

Типовий цикл тренування/інференсу має стадії, які можуть блокуватися незалежно:

  1. Джерело даних: об’єктне сховище, NFS, локальний NVMe, база даних, feature store.
  2. Декодування/аугментація: CPU-трансформації, декодування зображення, токенізація, стиснення.
  3. Передача хост→пристрій: pinned memory vs pageable, PCIe vs NVLink, асинхронні копії.
  4. GPU-обчислення: ядра, tensor-ядра, операції, обмежені пам’яттю, редукції.
  5. Пристрій→пристрій / колективи: multi-GPU all-reduce, шардована увага, pipeline-паралелізм.
  6. Чекпоінтинг/логування: латентність файлової системи, сплески метаданих, синхронні записи.

«Більший GPU» часто прискорює лише крок 4. Якщо крок 1 або 2 є вузьким місцем, крок 4 не має значення. Якщо на великому масштабі домінує крок 5, крок 4 стає незначущим.

2) Розумійте, що приховує «utilization»

nvidia-smi GPU-Util — це грубий індикатор: «GPU щось робив недавно?» Це не гарантія ефективної роботи. GPU може показувати 95% використання, виконуючи операції, обмежені пам’яттю, які ледве задіюють tensor-ядра, або при запуску дуже дрібних ядер з великими накладними витратами. Він може показувати 30% використання і водночас забезпечувати відмінну пропускну здатність, якщо навантаження є імпульсним і добре перекривається.

3) Великі GPU мають більший апетит

Коли ви переходите від меншого GPU до більшого, ви також підвищуєте мінімальну необхідну «швидкість годування»:

  • Більше обчислень: означає, що вам потрібні більші батчі, більше паралельної роботи, краще злиття ядер або більше конкурентності.
  • Більше пропускної здатності: означає, що вам потрібні патерни доступу до пам’яті, які можуть її використати; випадковий доступ ніяк не стане послідовним.
  • Більше VRAM: може зменшити CPU↔GPU перекази і дозволити більші моделі/батчі — але лише якщо ваш фреймворк використовує це розумно.

4) Затримки й накладні витрати не зменшуються

Накладні витрати на запуск ядра, інтерпретатор Python, синхронізації на крок, логування та координацію даталоадера можуть домінувати для малих моделей або малих батчів. Швидший GPU скорочує час обчислення, роблячи накладні витрати більшою часткою від загального часу. Вітаю: ви «оптимізували» себе в ще одне вузьке місце.

Жарт №1 (коротко і по суті): Великий GPU не виправить повільний даталоадер так само, як велика кружка кави не лікує безсоння.

5) Ефективність — це контракт стека

В термінах SRE ваш GPU є нижнім рівнем для: сховища, планування CPU, поведінки алокатора пам’яті, runtime-контейнера, драйверів, бібліотек і іноді планувальника кластера з власними правилами. Якщо будь-який шар порушує контракт — повільний I/O, галасливі сусіди, неправильне NUMA-pin, невірна версія CUDA — ви не «GPU-bound», ви «зв’язаний усім іншим».

Цікаві факти та історичний контекст (що справді має значення)

Це не тривія для вечірки. Кожен пункт відповідає реальному режиму відмови або архітектурному рішенню.

  1. GPU стали загального призначення випадково і завдяки наполегливості: ранні роботи з «GPGPU» використовували графічні API для обчислень до появи CUDA. Спадщина означає, чому патерни доступу до пам’яті все ще дуже важливі.
  2. Ставка CUDA 2007 року: модель програмування NVIDIA зробила обчислення на GPU доступними, але також створила екосистему, де невідповідності драйверів/тулкітів можуть тихо коштувати продуктивності.
  3. PCIe був повторюваним обмеженням: для багатьох навантажень вузьким місцем передача хост→пристрій залишалася навіть при стрибку обчислювальної потужності GPU.
  4. HBM змінила правила для моделей, чутливих до пропускної здатності: висока пропускна здатність пам’яті допомагає, але операції, обмежені пам’яттю, все ще вимагають локальності та коалесценції; пропускна здатність не виправить погані патерни доступу.
  5. Tensor-ядра змістили розмову про «оптимальний dtype»: змішана точність може бути трансформаційною, але вона не безкоштовна — масштабування втрат, числова точність і витрати на конверсію можуть вдарити.
  6. NVLink/NVSwitch з’явилися, бо PCIe було недостатньо: масштабування multi-GPU часто залежить від міжз’єднання й топології більше, ніж від голих FLOPS.
  7. Глибоке навчання зробило «input pipelines» першокласною проблемою продуктивності: люди дізналися важким шляхом, що декодування JPEG може стати вашим «вузьким місцем GPU». Хоча насправді це не GPU.
  8. Чекпоінтинг став складнішим із ростом моделей: збереження стану може перетворитися на проблему розподіленого зберігання та метаданих, а не просто «записати файл».

Швидкий план діагностики (перші/другі/третьі перевірки)

Перше: чи справді GPU голодує?

  • Подивіться на завантаження та використання пам’яті по процесах GPU.
  • Перевірте, чи споживання живлення GPU близьке до очікуваного під час стабільного стану.
  • Перевірте, чи CPU завантажені або %iowait високий.

Якщо GPU-Util низьке, а CPU/%iowait високий — припиніть звинувачувати GPU. Виправляйте «кормильця».

Друге: чи ви обмежені обчисленнями, пам’яттю чи запуском/накладними витратами?

  • Порівняйте досягнуті TFLOPS з теоретичними (приблизно) за допомогою метрик профайлера.
  • Перевірте пропускну здатність пам’яті GPU і індикатори заповнення SM.
  • Шукайте багато дрібних ядер, точки синхронізації або накладні витрати Python.

Якщо ви обмежені пам’яттю — «більше обчислень» мало допоможе. Якщо ви обмежені накладними витратами, швидший GPU лише погіршить ситуацію.

Третє: якщо це multi-GPU, чи не є міжз’єднання справжнім вузьким місцем?

  • Перевірте топологію: наявність NVLink, покоління PCIe, розміщення NUMA.
  • Перевірте час all-reduce у ваших трасах профайлера (NCCL).
  • Перевірте насичення мережі (для multi-node): RDMA, TCP, лічильники комутаторів.

Якщо колективні операції домінують, масштабування вгору до більшого GPU на вузол може бути кращим, ніж масштабування вшир — або навпаки — залежно від топології. Міряйте, не вгадуйте.

Практичні завдання: команди, виводи та рішення

Це тип перевірок, які можна виконати під час інцидент-бріджу без спеціального профайлера. Кожне завдання містить: команду, типовий вивід, що це означає і яке рішення прийняти.

Завдання 1: підтвердити видимість GPU, стан драйвера та базове навантаження

cr0x@server:~$ nvidia-smi
Wed Jan 21 12:11:03 2026
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15    Driver Version: 550.54.15    CUDA Version: 12.4               |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA A100-PCIE-40GB          On  | 00000000:65:00.0 Off |                    0 |
|  33%   62C    P0              185W / 250W |  12450MiB / 40536MiB |     38%      Default |
+-----------------------------------------+----------------------+----------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|=======================================================================================|
|    0   N/A  N/A     21983      C   python3                                     12340MiB |
+---------------------------------------------------------------------------------------+

Що це означає: версії драйвера/тулкіту, споживання потужності, використання пам’яті, util і який процес його займає.

Рішення: Якщо GPU-Util низький і потужність низька під час «стабільного стану», швидше за все GPU голодує або ви обмежені накладними витратами. Піднімайтесь вгору по стеку.

Завдання 2: спостерігати завантаження і потужність у часі (виявити голодування)

cr0x@server:~$ nvidia-smi dmon -s pucvmt
# gpu   pwr gtemp mtemp    sm   mem   enc   dec  mclk  pclk
# Idx     W     C     C     %     %     %     %   MHz   MHz
    0   92    56     -    12     8     0     0  1215  1410
    0  210    64     -    85    72     0     0  1215  1410
    0   75    55     -     9     6     0     0  1215  1410

Що це означає: сплески високого SM% за якими йдуть простої часто вказують на затримки в input pipeline або точки синхронізації.

Рішення: Якщо бачите «пилкоподібний» малюнок, досліджуйте даталоадер, копії хост→пристрій і конкуренцію CPU.

Завдання 3: визначити насичення CPU vs iowait (класичний збій кормильця)

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 	01/21/2026 	_x86_64_	(64 CPU)

12:12:11 PM  CPU   %usr %nice  %sys %iowait  %irq %soft  %steal  %idle
12:12:12 PM  all   420.0  0.0   18.0   60.0   0.0  2.0    0.0   0.0
12:12:12 PM   12    97.0  0.0    1.0    2.0   0.0  0.0    0.0   0.0
12:12:12 PM   13    95.0  0.0    3.0    2.0   0.0  0.0    0.0   0.0

Що це означає: високий %usr на багатьох ядрах свідчить про CPU-препроцесинг або накладні витрати Python; високий %iowait — про затримки сховища.

Рішення: Якщо %iowait високий — профілюйте сховище. Якщо %usr «пригвинчений», оптимізуйте декодування/токенізацію, збільште кількість worker-ів або виносьте трансформації на C/C++/нативний код.

Завдання 4: перевірити I/O на рівні процесів (чи повільний ваш датасет?)

cr0x@server:~$ iostat -xz 1 2
Linux 6.5.0 (server) 	01/21/2026 	_x86_64_	(64 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          30.12    0.00    2.10   22.45    0.00   45.33

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s  w_await aqu-sz  %util
nvme0n1         420.0   58240.0     0.0   0.00    8.10   138.67    15.0   2048.0    2.30   3.60  82.00

Що це означає: високий %util і високий r_await вказують, що пристрій зайнятий і читання очікують.

Рішення: Якщо сховище «гаряче», кешуйте датасети локально, обережно підвищуйте паралелізм читань або змінюйте формат (менше дрібних файлів).

Завдання 5: виявити «занадто багато дрібних файлів» (проблема з метаданими)

cr0x@server:~$ find /datasets/vision/train -type f | head -n 5
/datasets/vision/train/000001.jpg
/datasets/vision/train/000002.jpg
/datasets/vision/train/000003.jpg
/datasets/vision/train/000004.jpg
/datasets/vision/train/000005.jpg

Що це означає: датасети «одне зображення = один файл» можуть руйнувати продуктивність метаданих на мережевих файлових системах.

Рішення: Консолідуйте в шарди (tar/LMDB/record-файли) або стагуйте локально на NVMe.

Завдання 6: перевірити швидкість і ширину PCIe (тихий вбивця пропускної здатності)

cr0x@server:~$ nvidia-smi -q -d pcie | sed -n '1,80p'
==============NVSMI LOG==============

GPU 00000000:65:00.0
    PCIe Generation
        Current                       : 3
        Max                           : 4
    Link Width
        Current                       : x8
        Max                           : x16
    Tx Throughput                     : 1200 KB/s
    Rx Throughput                     : 980 KB/s

Що це означає: робота на Gen3 x8 замість очікуваного Gen4 x16 — це реальне зменшення пропускної здатності host→device.

Рішення: Перевставте карту, перевірте налаштування BIOS, перевірте riser-и, перевірте підключення слоту і переконайтеся, що лінії не діляться з іншими пристроями.

Завдання 7: перевірити NUMA-локальність (CPU годує GPU через правильний сокет)

cr0x@server:~$ nvidia-smi topo -m
        GPU0    CPU Affinity    NUMA Affinity
GPU0     X      0-31            0

Legend:
  X    = Self

Що це означає: ядра CPU 0–31 локальні для GPU0. Використання неправильного сокета може збільшити затримку і зменшити ефективну пропускну здатність.

Рішення: Прив’яжіть worker-ів даталоадера та основний процес до локального NUMA-вузла, коли це важливо для продуктивності.

Завдання 8: перевірити термальні або енергетичні тротлінги (невидиме гальмо)

cr0x@server:~$ nvidia-smi -q -d PERFORMANCE | sed -n '1,120p'
==============NVSMI LOG==============

GPU 00000000:65:00.0
    Performance State               : P2
    Clocks Throttle Reasons
        Idle                        : Not Active
        Applications Clocks Setting  : Not Active
        SW Power Cap                : Active
        HW Slowdown                 : Not Active

Що це означає: SW Power Cap: Active свідчить, що ваше завдання досягає ліміту потужності (або встановленого обмеження) і частота знижується.

Рішення: Налаштуйте ліміт потужності (якщо політика дозволяє), покращіть охолодження або прийміть, що ваш «більший» GPU не працюватиме на заголовкових частотах.

Завдання 9: підтвердити, що частота CPU не зафіксована низько (помилка конфігу хост-контейнера)

cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
powersave

Що це означає: CPU у режимі powersave можуть сповільнити препроцесинг і координацію, голодуючи GPU.

Рішення: Встановіть governor performance на виділених нодах для тренування (з урахуванням політики ops).

Завдання 10: перевірити hugepages / симптоми тиску pinned memory (зупинки H2D копій)

cr0x@server:~$ grep -E 'HugePages|MemAvailable' /proc/meminfo
MemAvailable:   18432000 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0

Що це означає: не є однозначним, але низька доступна пам’ять у поєднанні з інтенсивним даталоадингом може збільшити пейджинг і сповільнити трансфери.

Рішення: Зменшіть тиск на хост-пам’ять, розгляньте налаштування pinned memory і уникайте переповнення RAM надто великою кількістю worker-ів.

Завдання 11: перевірити ліміти CPU контейнера в cgroup (самонанесене голодування)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.max
200000 100000

Що це означає: квота 200ms на період 100ms фактично обмежує до еквіваленту 2 CPU. Для GPU-завдання це часто абсурдно.

Рішення: Збільшіть запити/ліміти CPU, щоб ваш input pipeline міг дихати; підбирайте CPU під клас GPU.

Завдання 12: перевірити голодування worker-ів даталоадера (симптоми на боці Python)

cr0x@server:~$ ps -o pid,pcpu,pmem,cmd -C python3 --sort=-pcpu | head
  PID %CPU %MEM CMD
21983 780.2 12.4 python3 train.py --config prod.yaml
22010  95.1  1.2 python3 -c from multiprocessing.spawn import spawn_main; spawn_main(...)
22011  94.6  1.2 python3 -c from multiprocessing.spawn import spawn_main; spawn_main(...)

Що це означає: кілька процесів worker-ів активні; якщо зайнятий лише основний процес, ваші worker-и можуть застрягати на I/O, роботі з GIL або бути неправильно налаштованими.

Рішення: Налаштуйте кількість worker-ів, перенесіть CPU-трансформації в нативний код, попередньо обчисліть фічі або використайте швидші формати датасетів.

Завдання 13: спостерігати пропускну здатність мережі для multi-node тренування

cr0x@server:~$ sar -n DEV 1 2 | grep -E 'IFACE|mlx|eth'
12:15:01 PM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s
12:15:02 PM       eth0   8200.00   7900.00  910000.00  880000.00
12:15:03 PM       eth0   8300.00   8100.00  920000.00  895000.00

Що це означає: якщо мережа насичена під час фаз all-reduce — масштабування сповільнюється.

Рішення: Якщо ви насичуєте мережу, перегляньте налаштування NCCL, топологію, компресію градієнтів або кількість вузлів.

Завдання 14: виявити сплески латентності файлової системи під час чекпоінтингу

cr0x@server:~$ dmesg | tail -n 8
[123456.120001] nfs: server nfs01 not responding, still trying
[123456.421019] nfs: server nfs01 OK
[123457.002341] INFO: task python3:21983 blocked for more than 120 seconds.

Що це означає: ваш крок тренування може бути в порядку; пауза викликана записом чекпоінта.

Рішення: Зробіть чекпоінтинг асинхронним, записуйте локально, а потім завантажуйте, або змініть бекенд сховища.

Де великі GPU програють: типові вузькі місця за рівнями

Input pipeline: прихована плата за пропускну здатність

Якщо ви тренуєте на зображеннях, відео або великих текстових корпусах, ви запускаєте фабрику:

  • Зчитування стиснутих байтів
  • Декодування (часто на CPU)
  • Трансформації/аугментації
  • Формування батчів, колатування, паддінг
  • Копіювання на GPU

Кожна стадія може бути «достатньо швидкою» для меншого GPU і раптово недостатньою для більшого. Оновлення не ламає код; воно ламає припущення про запас потужності.

Висококласні GPU підсилюють слабкі конвеєри. Це не метафора; це базова теорія черг. Коли час обслуговування на одній стадії зменшується, наступна стає вузьким місцем.

Накладні витрати на боці CPU: смерть від тисячі синхронізацій

Python чудовий для вираження намірів. Але він не чудовий для виконання дрібних частих операцій у тісному циклі при координації пристрою, що жере терафлопси. Якщо ваш цикл на крок включає логування, агрегацію метрик, часті синхронізації пристрою або нелекторизовану CPU-роботу, швидший GPU скоротить обчислювальний вікно і зробить CPU-накладні витрати пропорційно більш помітними.

Типові причини:

  • Виклики .item() або примусова синхронізація на кожному кроці
  • Часті зміни форм, що перешкоджають злиттю ядер
  • Занадто малі батчі (особливо в інференсі)
  • Надмірна препроцесинг на зразок per-sample у Python

Передача хост↔пристрій: PCIe — це не рекомендація

Коли говорять про «швидкість GPU», часто забувають про шинну підсистему. Пропускна здатність і затримка PCIe — властивості платформи, а не ваші бажання. Якщо ваша модель багато разів переміщує дані між CPU і GPU, або у вас є pageable копії й точки синхронізації, ви можете впертися в передачі задовго до обчислень.

Типова антипатерна: «Ми покладемо ембедінги на CPU, щоб зекономити VRAM». На великому GPU це може бути катастрофою.

Операції, обмежені пам’яттю: коли FLOPS не важливі

Деякі операції обмежені пропускною здатністю пам’яті або поведінкою кешу, а не обчисленнями. Подумайте про поелементні операції, певні шари нормалізації, пошук у словниках ембедінгів і патерни уваги, не оптимізовані під локальність. Ви можете купити більше обчислень і не побачити суттєвого покращення.

Сигнали:

  • Високий GPU-Util, але скромне споживання потужності від очікуваного
  • Профайлер показує низьке використання tensor-ядр
  • Пропускна здатність пам’яті близька до піку, поки SM-util здається «зайнятим», але не продуктивним

Multi-GPU: податки масштабування приходять швидко і не відходять

Multi-GPU тренування — це сварка з фізикою. У якийсь момент ви витрачаєте більше часу на координацію, ніж на обчислення. Великі GPU можуть допомогти, зменшивши кількість потрібних GPU для даного батча/моделі, що знижує накладні витрати колективу. Або вони можуть зашкодити, спонукаючи до більшої кількості вузлів із слабким міжз’єднанням, через що all-reduce домінуватиме.

Топологія має значення. NVLink проти PCIe. Розміщення за сокетами. Оверсабскрипція свитча. Якщо ви не знаєте свою топологію, ви не знаєте продуктивності.

Чекпоінтинг і спостережуваність: феномен «було добре, поки не збереглося»

Чекпоінтинг часто синхронний. Якщо кожен ранк пише або ви пишете на повільну спільну файлову систему, ваша робота виглядатиме «таємниче повільною», навіть якщо обчислення в порядку. Сплески латентності сховища перетворюються на простій GPU.

Жарт №2 (коротко і по суті): Чекпоінтинг на спільну файлову систему в години пік — чудовий спосіб емоційно дізнатися, що таке «зворотній тиск».

Три міні-історії з корпоративного світу (анонімізовано)

Міні-історія 1: Інцидент через неправильне припущення

Компанія розгорнула нові вузли з GPU, щоб прискорити повторне навчання рекомендера. Запит на зміну був простим: більші GPU, той самий код, той самий датасет. Припущення було комфортним: «Обчислення були вузьким місцем».

За кілька днів SLA навчання погіршився. GPU показували низьке завантаження, а канал on-call наповнився скриншотами простаючих прискорювачів і зовсім не простаючих рахунків у хмарі. Хтось звинувачував драйвери. Хтось — фреймворк. Звичне.

Фактична проблема була нуднішою: датасет зберігався на спільній мережевій файловій системі. Старі вузли мали менші GPU, які витрачали більше часу на крок, тож латентність файлової системи ховалася під час обчислень. Нові GPU так скоротили обчислення, що кожен крок натрапив на стіну читання/метаданих. Робота не стала швидшою; вона стала чутливішою.

Виправлення полягало в локальному стаджуванні даних: нічний rsync (або синхронізація з object-store) на локальний NVMe та шардінг дрібних файлів у більші блої. Використання піднялося, час навчання впав і інцидент припинився. Урок не в тому, що мережеві файлові системи погані. Урок: виміряйте pipeline наскрізь, перш ніж змінювати лише одну станцію.

Міні-історія 2: Оптимізація, що відбилася бумерангом

Інша команда оптимізувала використання пам’яті GPU, агресивно зменшивши розмір батчу і ввімкнувши gradient checkpointing скрізь. Використання VRAM виглядало чудово. Модель вмістилася, залишивши місце для росту. Всі тихо святкували — бо святкування теж накладні витрати.

Потім пропускна здатність впала. GPU були «зайняті», але час кроку зріс. Профайлер показав більше рекомпутації (очікувано) і потік дрібних ядер (менше очікувано). Менший батч збільшив накладні витрати на крок, зменшив можливості злиття ядер і погіршив ефективність all-reduce, бо відношення обчислення/комунікації впало.

Вони оптимізували не ту межу. Система не була VRAM-bound; вона була latency/overhead-bound. «Оптимізація пам’яті» віддала в обмін обчисленням, синхронізації та комунікації.

План відновлення був прагматичним: збільшити batch size, поки ядра знову не стануть «грубо-чіпкими», робити checkpoint лише для важливих шарів і використовувати змішану точність з ретельною валідацією. Використання пам’яті зросло. Пропускна здатність зросла більше. Бізнес-метрика — моделей, навчених за день — нарешті відповідала витратам на обладнання.

Міні-історія 3: Нудна, але правильна практика, що врятувала день

Команда платформи мала правило, яке всім заважало: кожне зображення вузла GPU мало невеликий «тест продуктивності» після провізії. Він перевіряв PCIe width, governor CPU, сумісність драйверів/бібліотек і базові тести пропускної здатності NCCL. Люди називали це бюрократією.

Одного тижня приїхала партія нових серверів. Завдання працювали, але multi-GPU продуктивність була жахливою на підмножині вузлів. Суїт одразу позначив ці вузли: GPU домовилися про нижчу ширину PCIe через проблему riser-а, а деякі вузли мали BIOS-налаштування, що лімітували швидкість лінії. GPU були в порядку. Платформа — ні.

Оскільки перевірки запускалися автоматично, команда ізолювала погані вузли, поки вони не забруднили флот. Ніякого затяжного інциденту, ніяких хибних підозр на «регресію фреймворка» у постмортемі з неправильним винуватцем.

Було нудно. Було правильно. І це врятувало дні інженерного часу — а це найдорогоцінніший ресурс у компанії.

Типові помилки: симптом → корінна причина → виправлення

1) Низький GPU-Util, високе використання CPU

Симптом: GPU стоїть на 10–40%, CPU завантажений.

Корінна причина: CPU-залежний препроцесинг (декодування/токенізація/аугментація), накладні витрати Python, замало worker-ів даталоадера.

Виправлення: Збільшіть кількість worker-ів, перенесіть трансформації в векторизовані/нативні операції, кешуйте попередньо оброблені дані, зафіксуйте правильний NUMA, переконайтеся, що ліміти CPU у контейнері не душать вас.

2) Низький GPU-Util, високий iowait

Симптом: провали простою GPU співпадають зі сплесками латентності сховища.

Корінна причина: датасет на повільному мережевому сховищі, занадто багато дрібних файлів, конфлікти метаданих.

Виправлення: шардуйте датасети, стагуйте на локальний NVMe, використовуйте read-ahead/caching, уникайте відкриття файлу на зразок per-sample, робіть чекпоінтинг асинхронним.

3) Високий GPU-Util, але розчаровуюча пропускна здатність

Симптом: 90–100% util, але час кроку не прекрасний.

Корінна причина: операції, обмежені пам’яттю, погане злиття ядер, неоптимізована увага, конверсії dtype.

Виправлення: використовуйте злиті ядра де можливо, налаштуйте батч/довжини послідовності для покращення ефективності, перевірте змішану точність, профілюйте, щоб знайти гарячі операції.

4) Продуктивність погіршується після апгрейду до «більшого» GPU

Симптом: новий GPU повільніший або лише трохи швидший.

Корінна причина: незмінений розмір батчу, через що накладні витрати домінують; PCIe лінк знижується; тротлінг потужності; неправильний governor CPU.

Виправлення: переналаштуйте batch size, підтвердіть Gen/width PCIe, перевірте причини тротлінгу, встановіть правильний governor CPU, перевірте NUMA-локальність.

5) Масштабування multi-GPU упирається після 2–4 GPU

Симптом: подвоєння GPU не подвоює пропускну здатність.

Корінна причина: all-reduce домінує, слабке міжз’єднання, погана топологія, малий батч на GPU.

Виправлення: збільшіть роботу на GPU, оптимізуйте колективні операції (розмір бакету), використайте менше вузлів з більшими GPU або покращіть мережеве/апаратне вирівнювання.

6) Випадкові уповільнення або «кожні 10 хвилин пауза»

Симптом: стабільне тренування переривається періодичними паузами.

Корінна причина: чекпоінтинг/логування, паузи GC, проблеми файлової системи, фонові компакти.

Виправлення: рознесіть чекпоінти в часі, записуйте локально, а потім завантажуйте, зменшіть синхронне логування, моніторте латентність файлової системи, уникайте точок концентрації метаданих.

Чеклісти / покроковий план

Покроково: як зробити так, щоб більший GPU справді був швидшим

  1. Встановіть базову метрику: виміряйте зображень/с, токенів/с або запитів/с на старому GPU з тим самим кодом і знімком датасету.
  2. Підтвердіть сантехніку платформи: версія драйвера, PCIe Gen/width, governor CPU, топологія NUMA, ліміти потужності.
  3. Перевірте кормильця: пропускну здатність/латентність сховища, стан worker-ів даталоадера, резерв CPU, тиск пам’яті.
  4. Перетюньте batch size: більший GPU часто хоче більші батчі; якщо не можете — очікуйте зменшення прибутку.
  5. Профілюйте один репрезентативний прогін: знайдіть топ-ядра, час простою CPU, H2D час, час колективів.
  6. Виправте найбільше вузьке місце: не «оптимізуйте все». Оберіть домінантний затор часу на стіні.
  7. Перевірте коректність: особливо при змішаній точності і злитих ядрах — мовчазні регресії точності реальні й небезпечні.
  8. Перевірте ефективність за долар: іноді два «менші» GPU з хорошим годуванням кращі за один гігантський з голодним pipeline.

Чекліст: чого уникати при покупці GPU

  • Купувати за піковими TFLOPS, не зіставивши вузьке місце вашого навантаження (обчислення vs пам’ять vs I/O vs комунікація).
  • Припускати, що VRAM вирішує всі проблеми; він вирішує деякі, але й створює інші (більші чекпоінти, довше стартування, дорожчі відмови).
  • Ігнорувати топологію міжз’єднання для multi-GPU (лінії PCIe, наявність NVLink, розміщення NUMA).
  • Ігнорувати CPU: слабкий CPU може голодувати потужний GPU.
  • Ігнорувати сховище: «датасет на спільному NFS» — не план продуктивності.

Чекліст: «нудні SRE-контролі», що тримають GPU-флот швидким

  • Нодні acceptance-тести: PCIe Gen/width, базові тести пропускної здатності, перевірки причин тротлінгу.
  • Стандартизовані образи з зафіксованою сумісністю драйверів/бібліотек.
  • Дашборди, що показують GPU util, потужність, memory BW, CPU iowait і латентність сховища.
  • Автоматичний карантин для нодів, що домовляються про нижчу ширину лінії або показують повторні Xid-помилки.
  • Runbook-и, специфічні для навантажень (тренування vs інференс vs пакетні embedding-завдання).

FAQ

1) Якщо моє завантаження GPU низьке, чи є GPU проблемою?

Зазвичай ні. Низьке використання часто є симптомом голодування: CPU-препроцесинг, сховище, мережа або синхронізація. Підтвердіть за споживанням потужності та CPU/%iowait.

2) Чому апгрейд до швидшого GPU зробив мою задачу гіршою?

Ви скоротили обчислювальний шматок, тож фіксовані накладні витрати (Python, координація даталоадера, I/O) стали домінувати. Великі GPU зменшують час обчислення; вони не зменшують ваші погані практики.

3) Чи завжди збільшення batch size — це відповідь?

Ні. Воно може покращити ефективність ядер і розподілити накладні витрати, але може зашкодити збіжності, збільшити пам’ять або змістити вузьке місце до комунікації. Тюньте за метриками, а не за вірою.

4) PCIe vs NVLink: коли це важливо?

Це важливо, якщо ви робите multi-GPU роботу на одному вузлі або часто передаєте дані host↔device. Якщо колективи або peer-to-peer передачі значні, топологія критична для продуктивності.

5) Змішана точність не прискорила. Чому?

Ви можете бути обмежені пам’яттю, накладними витратами або витрачати час на конверсії dtype. Або у вашій моделі є операції, що не виграють від tensor-ядр. Профілюйте, щоб перевірити, куди йде час.

6) Найшвидший спосіб зрозуміти, чи я обмежений сховищем?

Шукайте високий iowait і високе навантаження диска/мережі під час тренування, плюс пилкоподібні SM% на GPU. Потім підтвердіть за допомогою iostat і логів файлової системи.

7) Чому multi-GPU не масштабується лінійно?

Бо ви платите за координацію (all-reduce, синхронізації, відстаючі процеси). Додавання GPU збільшує комунікацію і зрештою вона домінує, якщо робота на GPU не зростає відповідно.

8) Купувати один великий GPU чи кілька менших?

Залежить від вашого вузького місця. Якщо ви обмежені комунікацією, менше, але потужніші GPU можуть бути кращими. Якщо вам потрібна пропускна здатність для багатьох незалежних задач, кілька менших GPU часто виграють операційно.

9) Чи може Kubernetes scheduling нашкодити ефективності GPU?

Так. Ліміти CPU, тиск пам’яті, галасливі сусіди і погана NUMA-алокація можуть голодувати GPU. Якщо ви ставите GPU-поди як безстанні веб-поди, ви відкриєте нові форми суму.

10) Яке «здорове» цільове завантаження GPU?

Однозначної відповіді немає. Для стабільного тренування 80–95% може бути нормою. Для імпульсного інференсу нижче використання може бути оптимальним, якщо SLO по латентності виконується. Слідкуйте за пропускною здатністю і хвостовою латентністю, а не за метрикою зарозумілості.

Практичні наступні кроки

Якщо ви збираєтеся купувати більші GPU або вже купили і шкодуєте, зробіть це в такому порядку:

  1. Запустіть швидкий план діагностики і захопіть 10-хвилинне вікно GPU util, потужності, використання CPU, iowait і статистики сховища/мережі.
  2. Підтвердіть базові параметри платформи: PCIe Gen/width, причини тротлінгу, NUMA affinity, governor CPU, ліміти CPU контейнера.
  3. Виправте кормильця перш ніж змінювати код моделі: шардуйте дані, стагуйте локально, зменшуйте накладні витрати дрібних файлів, додавайте паралелізм там, де це дійсно корисно.
  4. Потім тюньте навантаження: batch size, змішана точність, можливості злиття ядер, налаштування multi-GPU комунікацій.
  5. Інституціоналізуйте нудні перевірки: acceptance-тести нодів, дашборди, що показують голодування, і runbook, який називає топ-блокери, які ви реально бачили.

Висновок не в тому, «не купуйте великі GPU». Купуйте їх, коли навантаження того заслуговує. Але ставтеся до продуктивності GPU як до будь-якої іншої production-системи: вимірюйте вузьке місце, змінюйте одну річ, перевіряйте, повторюйте. Обладнання швидке. Системи повільні. Ось чому у вас є робота.

← Попередня
Планування місткості ZFS: проєктування зростання без перебудови
Наступна →
Таймінги RAM без болю: МГц проти CL і що купувати

Залишити коментар