Якщо ви керуєте продукційним ML, ви це відчували: лист від закупівель із словами «термін поставки GPU знову зсунувся»,
команда моделей, яка наполягає «це має бути CUDA», і фінансист, що питає, чому ваші «звичайні сервери» мають комплектуючі, що поводяться як рідкісні метали.
Питання не в тому, чи CUDA хороша (вона хороша). Питання в тому, чи може індустрія дозволити собі, щоб
один пропрієтарний стек був дефолтною операційною системою прискорених обчислень. «Анти-CUDA» звучить як мем.
Але це не мем. Це постава управління ризиком, яка поступово перетворюється на інженерну роботу.
Що насправді означає «анти-CUDA» (і чого це не означає)
«Анти-CUDA» — це неточна фраза. У корпоративних нарадах її використовують у трьох різних значеннях,
і їх плутання призводить до того, що ви випадково відвантажуєте повний перепис замість стратегії.
Це означає зменшення операційного ризику від одного постачальника
Якщо ваш флот для тренування, флот для inference, драйвери, базові образи контейнерів, інструменти профілювання та
розробницька мускулатура всі припускають runtime одного постачальника — то ви не просто «використовуєте GPU».
Ви запускаєте вертикально інтегровану платформу, якою не володієте повністю. Це може бути прийнятно. Поки не стане неприйнятно.
Це означає вимогу переносимості там, де вона реалістична
Переносимість не бінарна. Деякі рівні вже можуть бути переносними сьогодні (формати моделей, високорівневі фреймворки, патерни побудови контейнерів),
тоді як інші залишатимуться специфічними для постачальника роками (певні бібліотеки ядра, деякі патерни комунікацій, нішеві профайлери).
Робота «анти-CUDA» здебільшого про те, щоб зменшити зону «непереносимости» до невеликої та керованої.
Це не означає заміну CUDA на відчуття
Якщо ви коли-небудь намагалися ввести «уніфіковану абстракцію» в кодову базу, де бюджет продуктивності реальний,
ви знаєте фінал: усі обіцяють переносимість, потім перший великий клієнт захоче пропускну здатність,
і раптом з’явиться швидкий шлях, специфічний для постачальника… для всього.
Жарт №1: «Анти-CUDA стратегія» без бенчмарків — це як тренування пожежної евакуації, де всі погоджуються, що сходи необов’язкові.
Чому CUDA перемогла: стимули, ергономіка та жорстка математика інструментів
CUDA не перемогла тому, що NVIDIA написала гарний PDF. Вона перемогла, бо стек був когерентним, швидким і агресивно
продактізованим наскрізь: компілятор, бібліотеки, профайлинг, драйвери та послідовна ментальна модель для розробників.
В операційних термінах: менше невідомих невідомих.
Справжня відмінність: бібліотеки та передбачувана продуктивність
Історія CUDA не про «писати кернели». Більшість продукційних команд не пишуть кастомні кернели, якщо це не необхідно.
Історія — це cuBLAS, cuDNN, NCCL, TensorRT і те, що «дефолтний шлях» зазвичай досить хороший і іноді відмінний.
Середній MLOps-платформ є графом залежностей з уподобаннями; CUDA — це упереджена база.
Гравітація фреймворків
PyTorch, TensorFlow, JAX, XGBoost, LightGBM, сервери Triton для inference, кастомні розширення на C++ — ці екосистеми
накопичувалися навколо CUDA як шляху найменшого опору. Як тільки артефакти моделей, CI-образи та «золоті вузли»
залежать від CUDA, вартість переключення стає організаційною, а не технічною. Ці витрати найважче оплатити.
Закриті екосистеми перемагають, коли вони прибирають точки прийняття рішень
Відкриті системи часто «перемагають» на папері і програють у каналі інцидентів. Закрита екосистема дає менше виборів,
а це часто саме те, чого хоче продукція о 3 ранку.
Відкриті vs закриті екосистеми: де криється прив’язка
Люди говорять про прив’язку, ніби це тільки про API. У реальному житті прив’язка живе в місцях, що не з’являються
в діаграмах архітектури: версії драйверів, тулчейни компілятора, шари контейнерів, модулі ядра та точна форма «провалів продуктивності».
П’ять шарів «екосистеми» і які з них кусають
- Апаратний рівень: архітектура GPU, об’єм пам’яті, інтерконекти (PCIe, NVLink), підтримка SR-IOV/MIG.
- Рівень ядра/драйвера: поведінка драйвера + прошивки, сумісність модулів ядра, біль DKMS-перебудов.
- Рівень рантайму: CUDA runtime vs ROCm/HIP, виявлення пристроїв, семантика стрімів, алокатори пам’яті.
- Рівень бібліотек: GEMM/conv/attention кернели, комунікації (еквіваленти NCCL), інструменти квантизації.
- Рівень фреймворку/додатка: PyTorch/XLA/JAX, кастомні опи, inference-движки, моніторингові хуки.
«Відкритість» здебільшого допомагає на рівні фреймворку/додатка і іноді на рівні рантайму. Найбільший біль часто сидить
у драйверах і бібліотеках, бо саме там «ковалиться» продуктивність.
Що реалістично дає «відкритість»
Сумлінна відкрита екосистема дає вам:
- Кілька постачальників, що змагаються продуктивністю й постачанням.
- Чіткі шляхи відступу, коли ціни або доступність йдуть не туди.
- Більше очей на правильність тулчейну ( іноді) та більше варіантів інтеграції для downstream.
Чого вона не гарантує — так це що ваша модель працюватиме однаково швидко скрізь. Переносимість — це просто.
Переносимість продуктивності — дорога частина.
Факти та історичний контекст, які важливі в радах директорів
Ви не зробите зваженого рішення про «анти-CUDA», не згадавши, як ми сюди потрапили. Кілька конкретних фактів,
які постійно з’являються в реальних дискусіях про закупівлі та дорожні карти:
- CUDA стартувала в 2006 як платформа загального призначення для NVIDIA GPU; вона досить стара, щоби мати операційний фолклор.
- OpenCL 1.0 з’явився у 2008 з обіцянкою переносимості, але постачальники та якість тулінгу швидко розійшлися.
- cuDNN дебютував у 2014 і допоміг стандартизувати примітиви для deep learning; саме бібліотеки, а не компілятори, задали темп для більшості команд.
- NCCL став дефолтом для мульти-GPU тренувань у багатьох стеках, бо в колективах «працює» перемагає «переносимо».
- TensorRT змінив гру для inference пакетом оптимізацій графа + вибору кернелів + квантизації в практичний робочий процес.
- Траєкторія ROCm була нерівною — періоди швидкого прогресу чергувалися з тертям у сумісності та пакуванні.
- Apple MPS показав інший урок: закриті екосистеми можуть бути надзвичайно продуктивними, якщо платформа когерентна.
- SYCL дозрів як C++ single-source модель, що може таргетити кілька бекендів; це серйозно, але «серйозне» ≠ «домінантне».
- Цикли дефіциту GPU змінили поведінку: коли постачання стискається, переносимість перестає бути академічним питанням і стає планом бізнес-неперервності.
Хто просуває «анти-CUDA» і чому саме зараз
Цей поштовх — не єдиний рух. Це нагромадження стимулів.
Хмарні провайдери хочуть важелі впливу
Якщо ви продаєте обчислення, ви не хочете, щоб ваша продуктова лінійка була «те, що привезе один постачальник, коли привезе».
Тому ви інвестуєте в альтернативи, сумісні шари та диференційовані акселератори. Не тому, що вам не подобається CUDA,
а тому, що маржинальність і ланцюг постачання — це фізика.
Підприємства хочуть опціональність без перепису
Enterprise IT не хоче чуття «ми — CUDA-каманда» так само, як не хоче «ми — один постачальник SAN».
Це не ідеологічно. Це про переговорну силу, планування життєвого циклу та зменшення області ураження інцидентів, специфічних для постачальника.
Дослідники хочуть менше стін
Академія та open-source спільноти схильні просувати відкриті стандарти й переносні рантайми, бо це розширює участь.
Але складна частина — якість кернелів. Стандарти самі по собі не народжують швидкий код.
Економічний тригер: завантаження зустріло дефіцит
Коли GPU були «приємністю», можна було терпіти неефективність. Коли ваш рахунок за GPU схожий на другу зарплату,
ви починаєте вимірювати все: час кернелів, копії хост↔пристрій, накладку комунікацій і вартість очікування конкретного апаратного забезпечення.
Що реально ламається в продукційних GPU-стеках
В лабораторії «переносимість» — переважно питання компіляції. У продукції це питання режимів відмов.
Ось гарячі точки, які я бачу повторно, коли команди намагаються диверсифікуватися від CUDA або просто модернізувати стек.
Несумісності драйвера/рантайму
Більшість «виходів з ладу GPU» — не апаратні збої. Це матриці версій з гострими краями:
оновлення ядра, оновлення драйверів, дрейф базового образу контейнера або тихе перебудовування CUDA-розширення під іншу ABI.
Обриви продуктивності, що виглядають як баги
Ви переходите на інший бекенд, і раптом модель працює в 3× повільніше. Нічого не падає. Все «працює».
Це найгірша категорія: проходить функціональні тести і провалює бюджет.
Колективи та сюрпризи топології
Багато-GPU тренування часто обмежені комунікаціями, а не математикою. Топологія інтерконекту (PCIe root complex, NUMA,
NVLink-сітки) і поведінка бібліотек колективів диктують масштабування. Змінюєш постачальника — змінюється історія комунікацій.
Прогалини в спостережуваності
CUDA має зрілу профайлінг- і телеметричну практику, яку багато ops-команд інтерналізували. Альтернативні стеки можуть мати
інші лічильники, інші інструменти і інші «підводні камені» щодо значення метрик.
Один цитат, який має бути прикріплений до кожного плану міграції: Надія — не стратегія.
— генерал Гордон Р. Салліван
Три корпоративні міні-історії з польових боїв
Міні-історія 1: Інцидент через хибне припущення
Середня SaaS-компанія вирішила «зробити inference портативним». План звучав розумно: стандартизувати образи,
використовувати PyTorch з чистим lockfile залежностей і уникати кастомних CUDA-розширень. Вони побудували другий пул для inference
з не-NVIDIA акселераторами для економії та доступності.
Хибне припущення було тонким: вони думали, що «без кастомних розширень» означає «без бінарних залежностей, специфічних для постачальника».
Але одна залежність підтягла бібліотеку продуктивності, яка постачалася як попередньо зібрані wheel-колеса, таргетовані під конкретний runtime GPU.
CI пройшов, бо CI працював на CPU. Staging пройшов, бо staging мав NVIDIA-вузли. Production на новому пулі — ні.
Симптом був неприємний: поди стартували, а потім потрапляли в crash-loop з помилками динамічного лінкера. SRE спочатку обробляли це як питання Kubernetes —
image pulls, node taints, security contexts. Справжня несправність була в файловій системі контейнера: wheel містив бінарі, що очікували CUDA-бібліотеки, які відсутні.
Виправлення не було героїчним. Вони перебудували образи з явними екстрами для кожного бекенду і зробили «тип бекенду» першокласною віссю побудови.
Важливіше, вони додали preflight-завдання, яке імпортувало стек моделі і запускало warmup з одним батчем на кожному класі вузлів.
Урок на майбутнє: переносимість — це не «не писати CUDA». Переносимість — це «довести, що ваш граф залежностей не пронесе CUDA через задні двері».
Міні-історія 2: Оптимізація, що обернулася проти
Велика корпоративна команда ML-платформи переслідувала чисту мету: зменшити латентність inference і скоротити витрати на GPU.
Вони ввімкнули агресивну компіляцію графа і злиті кернели, пропускаючи свої моделі через оптимізований рантайм. Це блискуче спрацювало — на одному поколінні GPU.
Потім закупівля доставила змішану партію: той самий постачальник, але різні степпінги архітектури. Оптимізовані рушійні артефакти кешувалися і використовувалися
по всіх вузлах, бо «GPU — це GPU», казав pipeline розгортання. Латентність зросла, і деякі запити почали таймаутитись.
Корінь проблеми — інвалідація кешу, класичний лиходій з кращим маркетингом. Артефакти рушія були специфічні для архітектури.
На «нових» GPU рантайм падав на повільні шляхи або витрачав надто багато часу на перекомпіляцію при першому запиті, перетворюючи p99 на кошмар.
Вони виправили це, ключуючи кеші за властивостями пристрою (compute capability, версії драйверів/рантайму) і розігріваючи рушії під час розгортання,
а не під навантаженням клієнтів. Також вони перестали вважати оптимізацію разовою дією і почали трактувати її як контракт сумісності.
Урок для розмов про анти-CUDA: момент, коли ви оптимізували під один стек, ви створили неявний контракт. Зробіть його явним, або він вкусить вас.
Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію
Фінансово-сервісна компанія керувала мультиорендними GPU-кластерами. Нічого екстраординарного, просто постійний потік тренувань і пакетного inference.
Вони прагнули опціональності: NVIDIA для важкого тренування, альтернативні акселератори для частини inference і прискорення ETL.
Їхнє секретне знаряддя не був хитрий компілятор. Це була нудна дисципліна: кожен клас GPU-вузла мав закріплений «золотий образ» з
протестованою комбінацією ядра, драйвера і контейнерного рантайму. Зміни проходили через canary-пул з автоматизованими smoke-тестами:
виявлення пристрою, простий GEMM-бенчмарк, тест комунікацій і мінімальний прогін inference.
Якось тиждень, оновлення безпеки ядра зламало збірки DKMS для одного класу вузлів. Canary-пул спіймав це миттєво; production не побачив збою.
Вони відклали розгортання, застосували рекомендоване постачальником оновлення драйвера і продовжили.
Урок: диверсифікація виживана, коли дисципліна платформи реальна. Без неї «анти-CUDA» стає «розробкою, керованою інцидентами».
Практичні завдання: команди, виводи та рішення (12+)
Це та частина, яку більшість стратегічних документів пропускає: що ви робите в робочий вівторок, коли флот повільний, падає або мігрується.
Нижче конкретні завдання з реалістичними командами, прикладами виводів і рішенням, яке ви приймаєте з них.
Ці приклади припускають Linux-хости і суміш Kubernetes та bare metal. Налаштуйте під себе, але зберігайте сенс.
Завдання 1: Підтвердити видимість GPU (NVIDIA)
cr0x@server:~$ nvidia-smi -L
GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-2a3c...)
GPU 1: NVIDIA A100-SXM4-40GB (UUID: GPU-8f19...)
Що це означає: Драйвер може перелічити пристрої. Якщо це не вдається, нічого вище цього не має значення.
Рішення: Якщо GPU відсутні — зупиніться і виправте драйвер/ядро/апарат перед тим, як чіпати фреймворки.
Завдання 2: Перевірити пару драйвер/рантайм і їх здоров’я
cr0x@server:~$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14 Driver Version: 550.54.14 CUDA Version: 12.4 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| 0 A100-SXM4-40GB On | 00000000:41:00.0 Off | 0 |
+-------------------------------+----------------------+----------------------+
Що це означає: Драйвер завантажений; версія CUDA, що вказана, — це максимум, який підтримує драйвер, а не обов’язково те, що використовує ваш контейнер.
Рішення: Якщо контейнери мають новіший CUDA userland, ніж підтримує хост-драйвер, очікуйте помилки рантайму. Закріплюйте або оновлюйте свідомо.
Завдання 3: Підтвердити виявлення пристрою ROCm (AMD)
cr0x@server:~$ rocminfo | head -n 12
ROCk module is loaded
=====================
HSA System Attributes
=====================
Runtime Version: 1.14
System Timestamp Freq.: 1000.000000MHz
Signal Max Wait Duration:18446744073709551615 (0xffffffffffffffff) (ns)
Що це означає: ROCm-рантайм може спілкуватися з модулем ядра і HSA-шаром.
Рішення: Якщо ROCk/HSA не здорові — не звинувачуйте модель. Виправляйте модулі ядра, прошивку й підтримувані поєднання дистро/ядро.
Завдання 4: Перевірити завантаження GPU і чи ви compute-bound чи input-bound
cr0x@server:~$ nvidia-smi dmon -s pucvmet -d 1 -c 5
# gpu pwr gtemp mtemp sm mem enc dec mclk pclk rxpci txpci fb bar1
# Idx W C C % % % % MHz MHz MB/s MB/s % %
0 215 62 - 18 12 0 0 1215 1410 120 30 40 1
0 220 63 - 19 13 0 0 1215 1410 110 28 41 1
Що це означає: Низька SM-завантаженість з низьким PCIe вказує, що GPU недоотримує даних від CPU/dataloader або чекає синхронізації.
Рішення: Якщо SM низький, не купуйте більше GPU. Спочатку профілюйте pipeline вводу, CPU pinning і розмір батчу.
Завдання 5: Підтвердити топологію PCIe і локальність NUMA
cr0x@server:~$ nvidia-smi topo -m
GPU0 GPU1 CPU Affinity NUMA Affinity
GPU0 X NV1 0-31 0
GPU1 NV1 X 0-31 0
Що це означає: GPUs з’єднані через NVLink (NV1) і ділять той самий NUMA-нод/CPU-афінність.
Рішення: Якщо GPU на різних NUMA-нодах — прикріпляйте процеси і даталоадери відповідно або очікуйте «таємничі» проблеми зі масштабуванням.
Завдання 6: Виявити CPU-throttling, що маскується під повільний GPU
cr0x@server:~$ lscpu | egrep 'Model name|CPU\(s\)|Thread|NUMA node'
Model name: AMD EPYC 7543 32-Core Processor
CPU(s): 64
Thread(s) per core: 2
NUMA node(s): 2
Що це означає: У вас кілька NUMA-нодів; міжвузловий трафік пам’яті може шкодити даталоадерам і хост↔пристрій копіям.
Рішення: Якщо pipeline вводу чутливий до CPU/NUMA — застосуйте CPU/пам’ять pinning в оркестраторі.
Завдання 7: Перевірити плагін пристроїв Kubernetes та allocatable ресурси
cr0x@server:~$ kubectl describe node gpu-worker-12 | sed -n '/Allocatable:/,/Events:/p'
Allocatable:
cpu: 62
memory: 503842Mi
nvidia.com/gpu: 8
pods: 110
Що це означає: Нода рекламує GPU планувальнику; якщо тут 0 — плагін пристрою/драйверний стек зламаний.
Рішення: Якщо allocatable GPU неправильні — виправте провізію вузла перед дебагом подів.
Завдання 8: Перевірити, чи контейнер може доступитися до GPU (інтеграційний тест рантайму)
cr0x@server:~$ docker run --rm --gpus all nvidia/cuda:12.3.2-base-ubuntu22.04 nvidia-smi -L
GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-2a3c...)
GPU 1: NVIDIA A100-SXM4-40GB (UUID: GPU-8f19...)
Що це означає: Контейнерний рантайм коректно підключений для передачі GPU і образ має сумісні інструменти userland.
Рішення: Якщо це не вдається — ваша проблема в інтеграції контейнерного рантайму/драйвера, а не в додатку.
Завдання 9: Виявити приховані CUDA-залежності в Python-wheel
cr0x@server:~$ python3 -c "import pkgutil,sys; import torch; print(torch.version.cuda); print(torch.cuda.is_available())"
12.1
True
Що це означає: Ваш збірка PyTorch з підтримкою CUDA і бачить GPU.
Рішення: Якщо ви цілите не-CUDA бекенд — припиніть шипити CUDA-збірки за замовчуванням; розділяйте артефакти по бекенд-типах.
Завдання 10: Підтвердити, що NCCL може ініціалізуватися і бачить очікувані інтерфейси
cr0x@server:~$ env | egrep 'NCCL|CUDA' | head
NCCL_DEBUG=INFO
NCCL_SOCKET_IFNAME=eth0
Що це означає: Ваше оточення налаштоване, аби допомогти NCCL вибрати правильний NIC і виводити корисні логи.
Рішення: Якщо мульти-вузлове тренування нестабільне — увімкніть NCCL debug і перевірте вибір NIC перед тим, як звинувачувати «мережу».
Завдання 11: Виміряти затримки диска і файлової системи, що можуть голодувати GPU
cr0x@server:~$ iostat -xz 1 3
avg-cpu: %user %nice %system %iowait %steal %idle
18.21 0.00 2.91 9.44 0.00 69.44
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s w_await aqu-sz %util
nvme0n1 420.0 65536.0 0.0 0.00 6.20 156.0 110.0 8192.0 1.10 3.50 98.0
Що це означає: Високий %util і зростаючий r_await вказують, що накопичувач насичений; даталоадери будуть «стрибаючими».
Рішення: Якщо iowait ненульовий і диск завантажений під зав’язку — виправте staging/caching датасету перед тюнінгом кернелів.
Завдання 12: Перевірити пропускну здатність мережі і втрати пакетів (реальність мульти-вузлового тренування)
cr0x@server:~$ ip -s link show dev eth0 | sed -n '1,8p'
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
RX: bytes packets errors dropped missed mcast
9876543210 8123456 0 12 0 12345
TX: bytes packets errors dropped carrier collsns
8765432109 7345678 0 0 0 0
Що це означає: Втрачені RX-пакети можуть зламати продуктивність колективів і спричинити таймаути, що виглядають як «проблеми GPU».
Рішення: Якщо кількість дропів зростає під навантаженням — досліджуйте черги NIC, MTU, congestion control і конфігурацію комутаторів.
Завдання 13: Перевірити логи ядра на предмет скидань GPU і подій на кшталт Xid
cr0x@server:~$ sudo dmesg -T | egrep -i 'nvrm|xid|amdgpu|gpu reset' | tail -n 8
[Mon Jan 21 10:12:03 2026] NVRM: Xid (PCI:0000:41:00): 79, GPU has fallen off the bus.
[Mon Jan 21 10:12:04 2026] nvidia: GPU 0000:41:00.0: GPU recovery action changed from 0x0 to 0x1
Що це означає: Проблеми апаратні, живлення, прошивка або цілісність PCIe. Це не «баг фреймворку».
Рішення: Карантин вузла, перевірити живлення/кабелі/BIOS/прошивку і розглянути RMA, якщо повторюється.
Завдання 14: Виявити фрагментацію пам’яті GPU і тиск на алокації
cr0x@server:~$ nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv
memory.total [MiB], memory.used [MiB], memory.free [MiB]
40960 MiB, 39812 MiB, 1148 MiB
Що це означає: Ви близькі до межі; помилки алокації або примусові поведінки подібні до сторінгових можуть проявитися як стрибки латентності.
Рішення: Зменшити розмір батчу, увімкнути краще планування пам’яті або виділяти одну модель на GPU замість мультиплексування.
Завдання 15: Перевірити конфігурацію MIG (якщо використовується)
cr0x@server:~$ nvidia-smi -i 0 -q | sed -n '/MIG Mode/,/GPU Instance/p'
MIG Mode
Current : Enabled
Pending : Enabled
Що це означає: MIG увімкнено; ваші домени продуктивності й відмов тепер — «інстанси», а не цілі GPU.
Рішення: Переконайтеся, що планування, моніторинг і плани ємності враховують MIG; інакше ви неправильно прочитаєте завантаження й насичення.
Швидкий план діагностики: що перевіряти першим/другим/третім
Коли хтось каже «GPU повільні», зазвичай це один з чотирьох вузьких місць: обчислення, пропускна здатність пам’яті,
pipeline вводу або комунікації. Не гадати. Проводьте триаж як SRE.
Перше: чи пристрій взагалі здоровий і видимий?
- Запустіть
nvidia-smiабоrocminfo; перевірте відсутність пристроїв. - Перегляньте логи ядра на предмет скидань, помилок шини та панік драйвера.
- Якщо є помилки: ізолюйте вузол. Не «повторюй, доки пройде».
Друге: чи ми недовикористовуємо GPU?
- Використовуйте
nvidia-smi dmon(або еквіваленти постачальника) для спостереження за SM та пам’яттю. - Якщо SM низький: підозрюйте dataloader, CPU pinning, маленькі батчі, точки синхронізації або оверхед Python.
- Якщо пам’ять висока, а SM низький: підозрюйте кернел/бібліотеку, що зв’язані з пам’яттю, або погану ф’южн-реалізацію.
Третє: чи система годує GPU (зберігання, CPU, PCIe)?
- Перевірте
iostat -xzна предмет насичення дисків іmpstatна iowait CPU. - Підтвердьте топологію PCIe і афінність NUMA; прикріпляйте процеси відповідно.
- Перевірте швидкості хост↔пристрій передач (лічильники PCIe як грубий сигнал).
Четверте: якщо multi-GPU або multi-node, чи комунікації лімітують?
- Шукайте мережеві дропи, повторні передачі і насичення NIC.
- Увімкніть debug NCCL (або еквівалент) і підтвердьте вибір інтерфейсу.
- Перевірте топологію: наявність NVLink, схему PCIe-комутаторів і локальність NUMA.
П’яте: тільки потім обговорюйте варіанти переносимості «анти-CUDA»
Якщо ви не можете надійно виміряти, куди йде час на вашому поточному стеці, міграція стеків посилить хаос.
Переносимість — це функція. Спостережуваність — її передумова.
Типові помилки: симптом → корінна причина → виправлення
Ось повторні помилкові сценарії, коли команди переслідують цілі «анти-CUDA» або просто намагаються запустити змішаний флот GPU.
Кожен запис достатньо конкретний, щоб діяти.
1) «Воно працює в staging, але падає в новому пулі GPU»
Симптом: Помилки імпорту, відсутні спільні бібліотеки, illegal instruction або помилки ініціалізації рантайму.
Корінна причина: Прихована бінарна залежність, специфічна для постачальника (wheels, розширення, inference-движки) побудована для CUDA або іншої архітектури.
Виправлення: Розділяйте артефакти збірки по бекендах; додайте preflight для кожного класу вузлів, що імпортує та проганяє one-batch warmup на цільовому пулі.
2) «Та сама модель, той самий код, 2–5× повільніше на альтернативному бекенді»
Симптом: Немає помилок, лише жахлива пропускна здатність.
Корінна причина: Різниця в зрілості кернелів/бібліотек (GEMM/attention), інші швидкі шляхи розміщення пам’яті, відсутність ф’южну або субоптимальна підтримка точності.
Виправлення: Окремо бенчмаркуйте примітиви (GEMM/attention); налаштуйте параметри моделі (точність, fused ops); прийміть підлаштування під бекенд як частину контракту.
3) «Мульти-вузлове тренування випадково зависає»
Симптом: Процеси застрягають в all-reduce; таймаути; один ранк відстає.
Корінна причина: Неправильний NIC, втрата пакетів, невідповідність MTU або несумісність топологій між хостами.
Виправлення: Прив’яжіть комунікації до правильного NIC, перевірте лічильники лінків і проганяйте мікробенчмарки комунікацій в тому самому контейнерному образі, з яким тренуєте.
4) «Оновлення драйвера постійно ламає вузли Kubernetes»
Симптом: Ноди стають NotReady після патча ядра; GPU зникають з allocatable ресурсів.
Корінна причина: Збій DKMS-перебудови або несумісність ядро-драйвер; дрейф образів між групами вузлів.
Виправлення: Використовуйте закріплені золоті образи для кожного класу вузлів; canary-rollout; розглядайте ядро+драйвер+рантайм як єдине ціле.
5) «Спайки p99 після того, як ми ввімкнули компіляцію/оптимізацію»
Симптом: Холодні старти або випадкові перекомпіляції під навантаженням; попадання кешів.
Корінна причина: Кеш рушія ключується занадто вільно; артефакти повторно використовуються на несумісних комбінаціях пристрій/драйвер.
Виправлення: Ключуйте кеші за властивостями пристрою і версіями рантайму; розігрівайте під час деплою; зберігайте артефакти по класу вузла.
6) «GPU завантаження низьке, але CPU теж низький»
Симптом: Все здається в режимі простою; пропускна здатність усе одно погана.
Корінна причина: Наклад синхронізації, маленькі кернели, оверхед Python або надмірні хост↔пристрій трансфери.
Виправлення: Профілюйте timeline; збільшіть розмір батчу; ф’юзуйте опи; перенесіть препроцесинг на GPU; зменшіть трансфери.
Жарт №2: Абстракції, нейтральні до постачальника, чудові — поки вам не потрібен профайлер постачальника, щоб зрозуміти, чому нейтральна абстракція повільна.
Чеклісти / покроковий план
Якщо ви хочете справжню «анти-CUDA» позицію — мається на увазі опціональність без самонанесених відмов — робіть це поетапно.
Ставтеся до цього як до міграції бекендів зберігання: ви не починаєте з перепису додатка, ви починаєте з визначення інтерфейсів,
вимірювання продуктивності і контролю розгортання.
Крок 1: Визначте, що має бути переносним, а що може бути специфічним для постачальника
- Переносне: формат моделі (де можливо), API сервісу, схеми запит/відповідь, CI-тести, бенчмарк-харнесс.
- Дозволено специфічне: inference-движки, злиття кернелів, бібліотеки комунікацій, низькорівневі профайлери.
- Правило: специфічне для постачальника — ок, якщо воно модульне і вимірюване.
Крок 2: Зробіть «клас вузла» першокласною концепцією
- Явно маркуйте пули за типом прискорювача, інтерконектом, об’ємом пам’яті та версіями драйвера/рантайму.
- Припиніть деплоїти «один образ для всіх», якщо не можете це довести.
- Ведіть матриці сумісності в коді (CI), а не в таблицях (надії).
Крок 3: Побудуйте пайплайн артефактів, що знає про бекенди
- Створюйте окремі теги контейнерів для кожного бекенду (і для кожної великої версії рантайму, якщо потрібно).
- Проганяйте бекенд-специфічні smoke-тести: виявлення пристрою, one-batch inference, sanity-check пропускної здатності, тест пам’яті.
- Розривайте збірки, коли непотрібні wheel-колеса пробираються непомітно.
Крок 4: Встановіть бенчмарк-харнесс, що відображає продукцію
- Вимірюйте p50/p95/p99, а не лише середню пропускну здатність.
- Включайте холодний старт і поведінку при прогріві кешу.
- Використовуйте ті самі розміри батчів, довжини послідовностей і препроцесинг, що й у продукційному трафіку.
Крок 5: Впроваджуйте мульти-постачальника лише там, де економіка працює
- Тренування: найскладніше для портингування через комунікації і очікування зрілості кернелів.
- Inference: часто легше, якщо ви готові до побудови рушіїв під кожен бекенд і певного тюнінгу.
- Batch/ETL прискорення: хороший кандидат, якщо примітиви стандартні і вимоги до латентності м’якші.
Крок 6: Розгортайте як SRE, а не як маніфест
- Пробуйте нові пули канарками з реальними шматками трафіку.
- Встановіть автоматичні пороги відкату за рівнем помилок і хвостовою латентністю.
- Тримайте «відомо хороший» пул під рукою, поки вивчаєте нові режими відмов.
FAQ
1) Чи існує справді координований «анти-CUDA» рух?
Не в сенсі однієї організації з єдиним планом. Є конвергенція стимулів: тиск на вартість, обмеження постачання і бажання мати важелі торгу.
Результат виглядає як рух.
2) Чи означає «відкритий» автоматично «переносимий»?
Ні. Відкриті стандарти допомагають переносимості, але продукційна переносимість залежить від драйверів, пакування, бібліотек і дисципліни тестування.
Ви можете мати відкриті API й усе ще мати специфічні для постачальника провали продуктивності.
3) Який найбільший технічний бар’єр для виходу з CUDA?
Зрілість бібліотек і глибина тулінгу. Код на рівні фреймворка часто можна змусити працювати деінде з певними зусиллями, але зрівняти якість кернелів CUDA для вашого конкретного робочого навантаження — важко.
4) Чи ставити ставку на одну переносиму прошарок типу SYCL або OpenCL?
Ставте на архітектуру, а не на лозунг. Використовуйте переносимі шари там, де вони допомагають, але зберігайте аварійні виходи для оптимізованих під постачальника шляхів.
Ваша мета — операційна опціональність, а не ідеологічна чистота.
5) Чи легше диверсифікувати inference, ніж тренування?
Зазвичай так. Inference може терпіти компіляцію рушія під бекенд і часто менш чутливе до мульти-вузлових колективів.
Тренування — там, де перші проявляються проблеми з комунікаціями і крайові випадки кернелів.
6) Що стандартизувати, щоб зменшити прив’язку без втрати продуктивності?
Стандартизуйте інтерфейси: API моделі, контракт сервісу, build/test-harness і визначення класів вузлів.
Дозволяйте специфічні для постачальника реалізації за цими інтерфейсами, якщо вони дають реальну продуктивність.
7) Як уникнути прихованих CUDA-залежностей?
Трактуйте розв’язання залежностей як проблему ланцюга постачання. Будуйте образи per-backend, проганяйте імпорт-тайм перевірки
і запускайте one-batch GPU warmup на кожному класі вузлів в CI або pre-prod.
8) Яка найпоширеніша причина, чому проекти «анти-CUDA» провалюються?
Вони починаються як переписи. Успішні починаються з операційних контролів: бенчмарк-харнесс, закріплені образи, canary-rollout і модульні бекенди.
Переносимість заробляється дисципліною.
9) Чи можна отримати переваги анти-CUDA, не змінюючи постачальника апаратного забезпечення?
Так. Навіть у флотах тільки з NVIDIA ви можете зменшити прив’язку, роблячи збірки відтворюваними, ключуючи кеші правильно і не зв’язуючи продукт з однією комбінацією драйвер/рантайм.
Висновок: що робити наступного тижня, а не наступного десятиліття
Чи буде «реальний» рух проти CUDA? Так, але він не виглядатиме як масовий вихід. Він виглядатиме як запити від закупівель на опції,
платформи, що будують бекенд-усвідомлені пайплайни, і SRE, що наполягають на відтворюваних класах вузлів, бо вони втомилися від рулетки драйверів.
CUDA залишатиметься домінантною найближчим часом, бо це не лише API — це продукційна екосистема зі зрілими бібліотеками і тулінгом.
Практичний крок — не «воювати з CUDA». Практичний крок — припинити робити CUDA незміряною залежністю.
Наступні кроки, які ви можете виконати
- Проведіть інвентаризацію графа залежностей на предмет бінарів, специфічних для GPU, і визначте, де CUDA потрібна неявно.
- Визначте класи вузлів і закріпіть комбінації ядро/драйвер/рантайм для кожного класу з canary-rollouts.
- Побудуйте бенчмарк-харнесс, що вимірює хвостову латентність і поведінку холодного старту, а не лише пропускну здатність.
- Розділіть контейнерні артефакти по бекендах і додайте preflight warmup на кожному цільовому пулі.
- Виберіть одне робоче навантаження (зазвичай inference) як пілот перенесення і ставтеся до нього як до сервісу з SLO, а не як до наукового проекту.
Опціональність не безкоштовна. Але й застрягти дорого.