Суперком’ютери з дурними багами: масштабування проблем до Місяця

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

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

Якщо ваш код добре працює на 64 ядрах, а потім перетворюється на гарбуз на 4 096 — у вас не «проблема продуктивності».
У вас помилка масштабування. І помилки масштабування рідко гламурні. Вони часто нудні. Саме тому вони виживають.

Як виглядають помилки масштабування в реальних суперком’ютерах

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

На 8 вузлах недбала бар’єрна синхронізація — це похибка округлення. На 8 192 вузлах цей бар’єр стає податком, який ви платите на кожній ітерації,
а податкопримачем стає шторм пакетів. На 8 вузлах відкриття 10 000 файлів під час запуску — «досить швидко».
На 8 192 вузлах це стає розподіленою DDoS-атакою на сервер метаданих.

Складність у тому, що система часто виглядає «здоровою» зовні. CPU завантажені, задача не падає, лінки мережі вгору,
сховище «лише» на 40% заповнене. Тим часом ваш реальний час подвоюється при додаванні вузлів.
Це не містика. Це фізика, теорія черг і невинне припущення одного розробника про «ще один MPI_Allreduce».

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

Помилки масштабування часто «дурні», бо вони людського масштабу

  • Контрольні шляхи O(N), які мали бути O(1), перетворюються на прірву. All-to-all «збір діагностики» стає all-to-all похороном.
  • Один спільний ресурс (один лок, один лидируючий ранк, один таргет метаданих, один потік на вузлі управління) стає пляшковим горлом.
  • Значення за замовчуванням (TCP буфери, hugepages, ліміти планувальника, параметри stripe в Lustre) були обрані для «розумних» навантажень, а не для вашого хаосу.
  • Пробіли в спостережуваності перетворюють продуктивність на містицизм. Без таймінгів по ранках ви сперечаєтесь про привидів.

Жарт №1: Задача на 10 000 ядер — це така ж звичайна задача, просто з більшою кількістю можливостей помилитися.

Цитата, яку варто повісити на стіну

«Надія — це не стратегія.» — генерал Гордон Р. Салліван

Якщо ви керуєте продукційним HPC, ви можете бути оптимістичним щодо науки. Ви не можете бути оптимістичним щодо хвостової затримки,
спільних файлових систем або ймовірності того, що один вузол із 5 000 сьогодні зробить щось дивне.

Факти та історія, що справді важливі

Історія суперкомп’ютерів повна блискучих FLOPS-чисел. Операційні уроки — в сносках.
Кілька конкретних фактів і контекстів, які змінюють спосіб мислення про масштабування:

  1. Закон Амдала (1967) не став менш вірним, бо ми почали купувати GPU. Послідовні фракції все ще вбивають масштабування на великому числі ранків.
  2. Закон Густафсона (кінець 1980-х) пояснює, чому «слабке масштабування» може виглядати чудово, навіть коли «сильне масштабування» розвалюється. Не плутайте ці поняття.
  3. MPI існує з 1990-х; шаблони комунікації добре вивчені. Багато «нових» помилок масштабування — це старі помилки в контейнерах.
  4. Паралельні файлові системи, як-от Lustre, розділили метадані й дані; більшість катастрофічних уповільнень сьогодні — це патології метаданих, а не обмеження пропускної здатності.
  5. InfiniBand і RDMA зробили низьколатентні колективи можливими, але також полегшили насичення fabric штормами колективів.
  6. Системи епохи екзаскейлу сильно опираються на гетерогенні вузли (CPU + GPU). Це зміщує вузькі місця: локальність PCIe/NVLink і staging на хості — часті приховані вбивці.
  7. Чекпойнт/рестарт стали операційно обов’язковими з ростом розмірів задач; не можна вважати відмови «рідкісними», коли працює 20 000 компонентів годинами.
  8. Масштабування метаданих файлової системи стало першочерговим питанням, коли робочі потоки перейшли від кількох гігантських файлів до мільйонів дрібних (шарди для ML, ансамблеві програми, логи по ранках).
  9. Планувальники (PBS, Slurm тощо) перетворили суперкомп’ютери на спільні продукційні системи. Це принесло «фізику черг»: backfill, фрагментація та стан вузлів стали змінними продуктивності.

Основні режими відмов: обчислення, мережа, сховище, планувальник

1) Обчислення: найповільніший ранг задає темп

У тісно зв’язаному HPC продуктивність диктується хвостом. Один ранг потрапляє в бурю page fault-ів, або один сокет працює на нижчій частоті,
і вся задача чекає на наступній точці синхронізації. Тому «середнє завантаження CPU» — це брехня, яку ви розповідаєте собі, щоб почуватися краще.

Слідкуйте за:

  • Помилки NUMA-локальності: пам’ять алокована на неправильному сокеті, віддалена пропускна здатність, непередбачувані затримки.
  • Переприсвоєння потоків: значення OpenMP за замовчуванням, що множаться на MPI-ранки і руйнують кеші CPU.
  • Тротлінг частоти: обмеження потужності, термальне зниження частоти або зниження частоти через AVX, що змінює час на вузол.

2) Мережа: колективи не цікавляться вашими почуттями

Більшість «таємничих» колапсів масштабування — це витрати на комунікацію, що зросли швидше за ваші обчислення.
Колективи (Allreduce, Alltoall, Barrier) необхідні; водночас це найпростіший спосіб перетворити красиву fabric на парковку.

Слідкуйте за:

  • Шаблони all-to-all у FFT, транспозиціях та деяких ML-навантаженнях. Вони чутливі до топології й перевантаження.
  • Незбалансовані ранки, що роблять колективи повторюваними зупинками.
  • Налаштування MTU, кредитів або черг, що допомагали мікробенчмаркам, але дестабілізували реальний трафік.

3) Сховище: пропускна здатність рідко перша, що ламається

Помилки масштабування сховища часто — це конкуренція за метадані, замаскована під «повільний I/O».
Ви бачите це як: задачі зависають на старті, або «open() повільний», або «stat() займає вічність», або файлової системи виглядають нормально, але додаток застряг.

Слідкуйте за:

  • Мільйони дрібних файлів під час початку/закінчення задач (логи по ранках, виходи по завданнях, тимчасові файли).
  • Шторми чекпойнтів, коли багато ранків одночасно пишуть, насичуючи підмножину OST через погане striping.
  • Невідповідності кешування на клієнті: занадто агресивне кешування спричиняє конкуренцію й інвалідації, замало — викликає кругові запити метаданих.

4) Планувальник і операції: ваша задача — гість у переповненому готелі

Масштабування — це не лише всередині задачі. Воно в кластері.
Політики планувальника, стан вузлів і спільні сервіси (auth, DNS, реєстри контейнерів, сервери ліцензій) стають частиною профілю продуктивності.

Слідкуйте за:

  • Розміщення задач: розкидані вузли по островах або leaf-сьвічах, що збільшує кількість хопів і конкуренцію.
  • Фоновий шум: агенти моніторингу, оновлення ядра, неконтрольовані логи або сусідня задача, яка виконує «креативний» I/O.
  • Крихкість контрольної площини: перевантаження Slurmctld, повільні prolog/epilog скрипти або затримки автентифікації в масштабі.

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

Вам не додаткові бали за повільну діагностику. Коли велика задача спалює вузло-години, ви робите триаж як SRE:
визначте, чи ви обмежені обчисленнями, мережею або I/O, потім звузьте конкретний лімітуючий фактор.

Перше: підтвердьте, що означає «погано», і ізолюйте масштаб

  1. Відтворіть у двох масштабах: один, що «працює», і один, що ні (наприклад, 256 ранків проти 4096). Якщо проблема не змінюється зі збільшенням масштабу, це не помилка масштабування.
  2. Визначте метрику: час на ітерацію, час чекпойнта, час до рішення або ефективність задачі. Оберіть одну. Не відмахуйтесь.
  3. Перевірте фази: старт, стаціонарні обчислення, фази комунікації, чекпойнти I/O, фіналізація. Помилки масштабування часто ховаються в коротких фазах.

Друге: визначте домінуючий стан очікування

  1. CPU завантажений, але низький IPC вказує на пам’ять/NUMA або тротлінг векторних інструкцій.
  2. Великий час у викликах MPI вказує на мережу/колективи або незбалансованість.
  3. Великий час у open/stat/fsync вказує на патології шляху метаданих.
  4. Багато ранків простає на бар’єрах вказує на навантажувальну незбалансованість або один повільний вузол.

Третє: знайдіть патерн «одна повільна річ»

На масштабі один хворий вузол може зупинити всю задачу.
Не усереднюйте. Визначте викиди за ранком і за хостом.

  1. Знайдіть найповільніші ранки (таймінги додатка або профілювання MPI).
  2. Відобразіть ранки на хости (планувальник або mpirun mapping).
  3. Перевірте локальний стан вузла: помилки пам’яті, частота CPU, лічильники NIC, помилки клієнта файлової системи.

Четверте: перевірте, чи спільні сервіси не є прихованим вузьким місцем

DNS-таймаути, повільний LDAP, завантаження образів з реєстру контейнерів та витяги ліцензій можуть виглядати як «зависання додатка»,
коли множаться на тисячі вузлів. Кластер може бути «працюючим», але одночасно недоступним.

Жарт №2: Нічого так не будує командну роботу, як 4 000 вузлів, що чекають одного DNS-запиту.

Практичні завдання: команди, виходи, рішення (12+)

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

Завдання 1: Підтвердити розміщення задачі та список вузлів (Slurm)

cr0x@server:~$ scontrol show job 842193
JobId=842193 JobName=climate_step
   UserId=ana(14021) GroupId=hpc(14000) MCS_label=N/A
   Priority=12233 Nice=0 Account=research QOS=normal
   JobState=RUNNING Reason=None Dependency=(null)
   RunTime=00:18:42 TimeLimit=02:00:00 TimeMin=N/A
   NodeList=cn[1203-1234,1301-1366]
   NumNodes=98 NumCPUs=6272 NumTasks=6272 CPUs/Task=1
   TRES=cpu=6272,mem=1500G,node=98
   MinCPUsNode=64 MinMemoryNode=15000M MinTmpDiskNode=0

Що це означає: NodeList показує, чи отримали ви компактний блок або розкидану алокацію. Змішані діапазони можуть означати кілька мережевих островів.

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

Завдання 2: Перевірити ефективність задачі та виявити очевидні втрати (облік Slurm)

cr0x@server:~$ sacct -j 842193 --format=JobID,Elapsed,AllocCPUS,CPUTime,MaxRSS,AveCPU,State
       JobID    Elapsed  AllocCPUS    CPUTime     MaxRSS     AveCPU      State
842193        00:18:42      6272  19-11:05:24    2100Mc    00:08.2    RUNNING
842193.batch  00:18:42        64  00:19:10      320Mc     00:18.9    RUNNING

Що це означає: AveCPU низький відносно Elapsed вказує на багато чекань (MPI, I/O, незбалансованість). MaxRSS допомагає визначити запас пам’яті або ризик сторінгу.

Рішення: Якщо AveCPU суттєво нижчий за очікуване, профілюйте MPI або I/O; якщо MaxRSS близький до ліміту пам’яті вузла, очікуйте сторінг і повільні ранки.

Завдання 3: Знайти частоту CPU по вузлу та ознаки тротлінгу

cr0x@server:~$ sudo turbostat --quiet --Summary --interval 5 --num_iterations 1
Avg_MHz  Busy%  Bzy_MHz  TSC_MHz  IRQ  SMI  PkgTmp  PkgWatt  CorWatt
  1890    78.3    2415     2300  8120    0    84.0    265.4    202.1

Що це означає: Якщо Bzy_MHz низький під навантаженням або PkgTmp високий, може бути тротлінг. Busy% близько до 100% але низький MHz — підозріло.

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

Завдання 4: Швидко виявити помилки NUMA-розміщення

cr0x@server:~$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0-31
node 0 size: 256000 MB
node 0 free: 182340 MB
node 1 cpus: 32-63
node 1 size: 256000 MB
node 1 free: 190112 MB
node distances:
node   0   1
  0:  10  21
  1:  21  10

Що це означає: Відстані показують штраф за віддалений доступ. Якщо процеси працюють на node 0, а пам’ять алокована на node 1, ви платите у пропускній здатності й затримці.

Рішення: Прив’язуйте ранки/потоки й пам’ять узгоджено (наприклад, Slurm –cpu-bind, numactl або OpenMP affinity). Перевірте продуктивність по ранках.

Завдання 5: Виявити чергу виконання CPU та iowait на вузлі

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

12:10:11 PM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:10:12 PM  all   61.2  0.0   6.1    18.9  0.0   1.2    0.0  12.6
12:10:12 PM    0   55.0  0.0   5.0    29.0  0.0   1.0    0.0  10.0

Що це означає: Високий %iowait вказує, що CPU блокується в очікуванні I/O. Це не «диск зайнятий», а «процес заблокований».

Рішення: Якщо iowait піки під час чекпойнтів, перевірте пропускну здатність файлової системи й налаштування stripe; якщо під час обчислень — шукайте сторінг або виклики метаданих файлової системи.

Завдання 6: Підтвердити, чи відбувається сторінг (класичний генератор повільних ранків)

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
12  0      0 182340  4212  81234    0    0     0    12 5200 8100 62  6 13 19
10  2   8192  10240  3900  22000  120  240  1024  2048 6100 9900 41  7  8 44

Що це означає: Ненульові si/so (swap-in/out) під навантаженням — погана новина. Навіть «трохи swap» у масштабі створює страгглерів.

Рішення: Зменшіть пам’ятний слід, збільшіть запит пам’яті на вузол, виправте витоки або відкоригуйте розмір задачі. Якщо лише один вузол свапить — підозрівайте брак DIMM або неправильно налаштовані cgroup-ліміти.

Завдання 7: Перевірити незбалансованість ранків MPI за допомогою простого файлу таймінгів

cr0x@server:~$ awk '{sum+=$2; if($2>max){max=$2; rmax=$1} if(min==0||$2

Що це означає: Великий розкид max/min кричуще вказує на незбалансованість або повільний вузол. Ранк з max — ваш ціль для розслідування.

Рішення: Відобразіть повільний ранг на хост; перевірте стан вузла, NUMA-розташування та помилки NIC/сховища на цьому хості.

Завдання 8: Відобразити ранки на вузли (стиль Slurm + mpirun)

cr0x@server:~$ srun -j 842193 -N 1 -n 1 hostname
cn1203

Що це означає: Підтвердіть, що ви можете таргетувати конкретні вузли з алокації. Це знадобиться для опитування викидів.

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

Завдання 9: Перевірити лічильники портів InfiniBand на помилки та затори

cr0x@server:~$ ibstat
CA 'mlx5_0'
	CA type: MT4123
	Number of ports: 1
	Port 1:
		State: Active
		Physical state: LinkUp
		Rate: 200
		Base lid: 1043
		SM lid: 1
		Link layer: InfiniBand
cr0x@server:~$ perfquery -x -r 1 | egrep 'PortXmitWait|PortRcvErrors|PortXmitDiscards'
PortXmitWait....................: 000000000000a1f2
PortRcvErrors...................: 0000000000000000
PortXmitDiscards................: 0000000000000003

Що це означає: PortXmitWait вказує на затори (очікування на відправлення). Discards означають втрати; не є нормою в стабільному стані.

Рішення: Якщо лічильники заторів ростуть під час повільних фаз, перевірте розміщення задачі/топологію та алгоритми колективів; якщо помилки/discard-и ростуть на одному вузлі, підозрюйте кабель/NIC/порт комутатора.

Завдання 10: Перевірити використання лінку і сброси на Ethernet для мережі керування або сховища

cr0x@server:~$ ip -s link show dev eno1
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    RX:  bytes packets errors dropped  missed   mcast
    9876543210  8123456      0     120       0  10022
    TX:  bytes packets errors dropped carrier collsns
    8765432109  7345678      0      42       0      0

Що це означає: Втрачені пакети в масштабі можуть проявлятися як «рандомна повільність», особливо для сервісів контрольної площини і NFS-домашніх директорій.

Рішення: Якщо кількість drop-ів зростає, перевірте кільця NIC, драйвер/прошивку та черги комутатора; розгляньте переміщення шумного трафіку з спільних лінків керування.

Завдання 11: Виявити гарячі точки метаданих на клієнті Lustre

cr0x@server:~$ lctl get_param -n llite.*.stats | egrep 'open|close|statfs|getattr' | head
open                                   1209341 samples [usec] 1 10 25 100 250 1000 2000 5000 10000 50000
close                                  1209340 samples [usec] 1 10 25 100 250 1000 2000 5000 10000 50000
getattr                                883201 samples [usec] 1 10 25 100 250 1000 2000 5000 10000 50000
statfs                                  12012 samples [usec] 1 10 25 100 250 1000 2000 5000 10000 50000

Що це означає: Величезні лічильники open/getattr вказують, що додаток «заламує» метадані. Відріки латентності (якщо розгорнуті) показують, чи ці виклики повільні.

Рішення: Якщо метадані гарячі, зменшіть кількість файлів, використайте агрегацію на вузлі, уникайте логів по ранках і розгляньте хешування/stripe-практики, що підтримуються вашою файловою системою.

Завдання 12: Перевірити стан OST/MDT і сигнали насичення (серверна сторона Lustre)

cr0x@server:~$ lctl get_param -n obdfilter.*.kbytesavail | head
obdfilter.fs-OST0000.kbytesavail=912345678
obdfilter.fs-OST0001.kbytesavail=905123456
obdfilter.fs-OST0002.kbytesavail=887654321
cr0x@server:~$ lctl get_param -n mdt.*.md_stats | head
mdt.fs-MDT0000.md_stats:
open                      39123890
close                     39123888
getattr                   82012311
setattr                    1023311

Що це означає: Дисбаланс ємностей може створювати непередбачувані гарячі точки. Висока інтенсивність MDT-кліків корелює з «повільним стартом» і «зависанням» при завершенні.

Рішення: Якщо один OST значно заповнений, зробіть ребаланс або відкоригуйте striping; якщо MDT-операцій багато під час шторму задач, налаштуйте поведінку клієнтів і виправте патерни файлів у додатку.

Завдання 13: Спостерігати I/O по процесу за допомогою pidstat

cr0x@server:~$ pidstat -d -p 21344 1 3
Linux 5.15.0 (cn1203) 	01/22/2026 	_x86_64_	(64 CPU)

12:12:01 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
12:12:02 PM  14021     21344      0.00  51200.00      0.00      87  climate_step
12:12:03 PM  14021     21344      0.00  48000.00      0.00      91  climate_step

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

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

Завдання 14: Виявити патологію «дрібного I/O» за допомогою iostat

cr0x@server:~$ iostat -dxm 1 2
Linux 5.15.0 (cn1203) 	01/22/2026 	_x86_64_	(64 CPU)

Device            r/s     w/s    rMB/s    wMB/s  avgrq-sz  avgqu-sz   await  svctm  %util
nvme0n1          0.0   3200.0     0.0     48.0     30.7       9.4     3.1    0.2   64.0

Що це означає: Багато IOPS з маленьким avgrq-sz означає дрібні записи. Await росте, коли черга наростає. %util показує насичення пристрою.

Рішення: Якщо це локально на вузлі, виправте буферизацію та пакетування; якщо це спільний монтуваний том, патерн масштабуватиметься — агрегуйте записи й вирівнюйте розміри I/O.

Завдання 15: Знайти системні виклики-гарячі точки (open/stat/fsync) за допомогою зведення strace

cr0x@server:~$ strace -c -p 21344 -f -q -e trace=openat,statx,futex,fsync,close -t -o /tmp/strace.out
strace: Process 21344 attached
^Cstrace: Process 21344 detached
cr0x@server:~$ tail -n 8 /tmp/strace.out
% time     seconds  usecs/call     calls    errors syscall
 62.14    1.840231          92     20007      102 openat
 18.77    0.555880          71      7821        0 statx
 11.02    0.326112          40      8099        0 futex
  8.07    0.239055         310       771        0 fsync

Що це означає: Якщо openat/statx домінують — інтенсивність метаданих. Якщо futex домінує — можлива конкуренція локів. Якщо fsync домінує — ви платите за гарантію стійкості.

Рішення: Для метаданих — зменште кількість файлових операцій і використайте менше файлів; для futex — рефакторіть багатопоточність або зменшіть спільні локи; для fsync — пакетування синхронізацій або рідша гарантія стійкості.

Завдання 16: Перевірити логи ядра та клієнта файлової системи на «м’які» помилки

cr0x@server:~$ dmesg -T | tail -n 12
[Thu Jan 22 12:06:41 2026] Lustre: llite fs-ffff8c2b3c2c8800: server not responding, reconnecting
[Thu Jan 22 12:06:43 2026] Lustre: llite fs-ffff8c2b3c2c8800: Connection restored
[Thu Jan 22 12:08:10 2026] mlx5_core 0000:41:00.0: CQE error: syndrome 0x2 vendor syndrome 0x0
[Thu Jan 22 12:08:10 2026] mlx5_core 0000:41:00.0: Dumping QP 0x1a2b

Що це означає: Транзієнтні реконекти та CQ-помилки NIC можуть створювати повільні ранки без жорсткої відмови. Це ті «дурні» баги, що псують графіки масштабування.

Рішення: Якщо лише підмножина вузлів показує це, видаліть їх з планувальника і відкрийте апаратний тікет; якщо широко — підозрюйте інцидент fabric/файлової системи.

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

Коротка історія 1: Неправильне припущення (і податок MPI_Barrier)

Дослідницька група принесла нову симуляцію в корпоративний HPC-кластер — серйозний код, серйозна наука, серйозний бюджет.
Розробники перевірили коректність на скромному розрізі і попросили «стільки вузлів, скільки можете», щоб вкластися в дедлайн.

Початковий запуск добре масштабувався до кількох сотень ранків. Після цього продуктивність пішла вбік.
Задача не падала; просто перестала прискорюватись. Користувачі звинувачували «мережу».
Мережа звинувачувала «код». Кожен був наполовину правий — найгірший вид правоти.

Профілювання показало шокуючу частку часу в MPI_Barrier. Це не завжди злочин,
але це ніколи не комплімент. Справжній винуватець — припущення: блок «debug timing», що збирав
таймінги по ранках на кожній ітерації і серіалізував вихід через ранк 0. Це мало бути тимчасово.
Воно стало постійним через інерцію.

У малому масштабі вартість бар’єра була прихована під обчисленнями. У великому масштабі бар’єр став глобальною
точкою синхронізації, яка підсилювала незначні незбалансованості у великі затримки. Ранк 0 додатково форматував
і записував логи. «Тимчасовий debug» перетворився на генератор дисбалансу навантаження.

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

Коротка історія 2: Оптимізація, що відбилася (широкий striping, реальна конкуренція)

Команда платформи намагалася допомогти аналітичному навантаженню, що писало великі чекпойнт-файли.
Хтось запропонував збільшити striping Lustre по багатьох OST, щоб «отримати більше пропускної здатності».
Це спрацювало в маленькому бенчмарку. Вони застосували це широко через модуль, який встановлював striping
за замовчуванням для проєктного каталогу.

А потім настала продукція. Навантаження не писало один великий послідовний файл на задачу.
Воно писало багато середніх файлів і часто робило операції з метаданими. Широке striping збільшило кількість
координації, яку файловій системі довелося виконувати. Це також підвищило ймовірність, що хоча б один OST буде зайнятий,
перетворивши хвостову затримку на фактор контролю.

Найболючіший симптом: задачі стали непередбачуваними. Деякі виконувались нормально, деякі повзли.
Користувачі робили те, що роблять користувачі — перенаправляли. Це множило навантаження і робило кластер схожим на привида.

Діагностика прийшла з кореляції повільних періодів і дисбалансу навантаження OST та підвищених операцій з метаданими.
«Більше stripe-ів» збільшило вентилювання по OST і конкуренцію, що нашкодило, коли патерн доступу не був потоковим I/O. Оптимізація вирішила лабораторну проблему і створила проблему в продакшені.

Вони відкотили дефолтне striping, надали розумні поради під навантаження і додали запобіжник:
задачі, що використовували шаблон директорії проєкту, мали попередню перевірку розмірів файлів і рекомендованих значень stripe.
Файлова система не стала швидшою; вона стала передбачуваною знову. Передбачуваність важливіша за швидкість у спільних системах.

Коротка історія 3: Нудна практика, що врятувала ситуацію (виведення проблемних вузлів)

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

Команда операцій мала нудну звичку: вони відстежували «підозрілі вузли» на основі низькорівневих лічильників,
а не лише жорстких відмов. Вузол, що логував повторювані CQ-помилки NIC або реконекти файлової системи, потрапляв у список спостереження.
Після досягнення порогу його виводили з планування й тестували офлайн.

Під час інциденту вони відобразили найповільніші ранки MPI на хости і знайшли патерн:
невелика група вузлів повторно давала стрегглери. Ці вузли не мали драматичних помилок — лише маленькі, часті попередження.
Достатньо, щоб уповільнити один ранг. Достатньо, щоб зупинити тисячі.

Вони вивели вузли з планування, перезапустили навантаження, і «баг додатка» зник.
Пізніше вони замінили апаратні частини. Команда науки отримала результати вчасно.
Практика не була хитрою. Вона була дисциплінованою.

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

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

1) Симптом: додавання вузлів робить задачу повільнішою

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

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

2) Симптом: задача «зависає» при запуску або в кінці

Корінна причина: Шторм метаданих: тисячі ранків виконують stat/open/unlink в одній директорії, або конкуренція на спільних Python середовищах/модулях.

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

3) Симптом: періодичні багатохвилинні зупинки під час стаціонарних обчислень

Корінна причина: Вибухи чекпойнтів або фонові події відновлення файлової системи; або «гучні сусіди», що насичують спільні OST/MDT.

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

4) Симптом: лише деякі запуски повільні; повторний запуск «виправляє»

Корінна причина: Хвостові проблеми: один деградований вузол, помилки портів комутатора, незбалансоване розміщення або OST, що гарячіший за інші.

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

5) Симптом: високе завантаження CPU, але малий прогрес

Корінна причина: Спін-вейт, конкуренція локів або агресивне busy-polling в стеку MPI; іноді неправильна змінна середовища викликає надмірне опитування.

Виправлення: Профілюйте з perf і зведенням strace; налаштуйте параметри прогресу MPI; зменшіть поділ локів; перегляньте прив’язку потоків.

6) Симптом: високий iowait на вузлах обчислень під час фази «обчислення»

Корінна причина: Прихований I/O: сторінг, логування, виклики метаданих або часте читання спільних конфігів.

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

7) Симптом: мережа виглядає нормально, але час MPI величезний

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

Виправлення: Використайте профілювання MPI для знаходження виклику; спробуйте альтернативні алгоритми колективів (якщо MPI це дозволяє); застосуйте розміщення з урахуванням топології; зменшіть частоту all-to-all.

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

Чекліст A: Коли велика задача погано масштабується (перша година)

  1. Отримайте дві точки даних: запуск у «хорошому масштабі» і запуск у «поганому масштабі» з тими ж входами й збіркою.
  2. Підтвердіть розміщення вузлів і чи алокація фрагментована.
  3. Розподіл часу: обчислення vs MPI vs I/O vs «інше». Якщо не можете розбити — додайте мінімальну інструментацію.
  4. Знайдіть найповільніші ранки і відобразіть їх на хости.
  5. Перевірте ці хости на тротлінг, свап, помилки NIC, реконекти файлової системи.
  6. Шукайте шторми метаданих: лічильники open/stat/unlink, логи по ранках, тимчасові файли.
  7. Перевірте залежності спільних сервісів: DNS/LDAP, завантаження контейнерів, сервери ліцензій.
  8. Вносьте по одному зміні. Помилки масштабування люблять сплутуючі змінні.

Чекліст B: Перевірка здоров’я сховища для HPC-навантажень

  1. Виміряйте кількість файлів і структуру директорій перед запуском у масштабі.
  2. Підбирайте кількість stripe під розмір файлу і патерн доступу (стрімінг vs дрібний випадковий I/O).
  3. Уникайте створення файлів по ранках у спільних директоріях.
  4. Пишіть менше, більші файли; пакетизуйте операції з метаданими.
  5. Рознесіть чекпойнти в часі; не дозволяйте 10 000 ранків fsync одночасно, якщо вам не до вподоби хаос.
  6. Моніторьте MDT-операції та завантаження OST, а не лише «загальну пропускну здатність».

Чекліст C: Мережа і MPI — базова перевірка

  1. Виміряйте час у викликах MPI (не лише загальний час виконання).
  2. Визначте колективні гарячі точки (Allreduce, Alltoall) і їх частоту.
  3. Перевірте лічильники fabric на затори і помилки; ізолюйте до вузлів чи системно.
  4. Переконайтесь у коректності прив’язки ранків/потоків; помилки NUMA можуть маскуватись під мережеві проблеми.
  5. Використовуйте розміщення з урахуванням топології для великих задач; уникайте покриття островів без потреби.

Чекліст D: Нудні операції, що запобігають «таємничій повільності»

  1. Виводьте вузли з повторюваними коректованими помилками або попередженнями NIC, а не тільки при жорстких відмовах.
  2. Тримайте прошивки й драйвери уніфікованими по кластеру; гетерогенність породжує хейсенбаґи.
  3. Встановіть і дотримуйте розумних значень за замовчуванням для модулів середовища (кількість потоків, pinning, бібліотеки I/O).
  4. Запускайте регулярні автоматизовані мікробенчмарки для мережі та файлової системи, щоб мати базові показники.

Поширені запитання

1) Чому продуктивність погіршується, коли я додаю вузли?

Тому що ви сплачуєте зростаючі витрати на координацію (комунікація, синхронізація, конкуренція за метадані), які переважають вигоди від обчислень.
Сильне масштабування має межу; знайдіть її і перестаньте робити вигляд, що її немає.

2) Як зрозуміти, чи я обмежений мережею або просто незбалансований?

Якщо час MPI великий і кілька ранків постійно повільніші — часто це дисбаланс або повільний вузол.
Якщо всі ранки проводять схожий час у колективах і лічильники fabric показують затори — це мережа/топологія.

3) Чи паралельна файлова система «повільна», чи мій додаток робить щось дурне?

Порахуйте операції з метаданими і дрібний I/O. Якщо ви створюєте мільйони файлів, повторно stat-ите ті самі шляхи
або пишете дрібні шматки з fsync, ви зробите будь-яку файлову систему повільною.

4) Який найшвидший спосіб виявити шторм метаданих?

Шукайте масивні лічильники open/stat/getattr і скарги користувачів про затримки старту/фіналізації.
На Lustre клієнтські stats і MDT md_stats — ваша рання система попередження.

5) Чому колективи MPI стають прірвою в масштабі?

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

6) Чи завжди варто збільшувати striping у Lustre, щоб отримати більше пропускної здатності?

Ні. Широке striping може допомогти при великих послідовних I/O, але може нашкодити при багатьох середніх файлах, змішаних патернах доступу
або коли це підсилює конкуренцію і хвостову затримку. Вимірюйте і підлаштовуйте під навантаження.

7) Яка найпоширеніша «дурна помилка» в HPC-додатках?

Пер-рank логування і тимчасові файли в спільній директорії, особливо при старті й завершенні.
Це операційний еквівалент того, що всі намагаються вийти через одну двері.

8) Як боротися з проблемами, що «виникають лише в масштабі»?

Ставтесь до цього як до інциденту SRE: відтворіть у двох масштабах, ізолюйте фази, знайдіть викидні ранки/хости і зіставте з системними лічильниками.
Потім прибирайте по одній змінній, поки прірва не зникне.

9) Що важливіше: пікова продуктивність чи передбачуваність?

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

Висновок: практичні подальші кроки

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

Наступні кроки, які реально дають результат:

  1. Інструментуйте додаток таймінгами по фазах і по ранках, щоб бачити незбалансованість і очікування.
  2. Прийміть швидкий план діагностики і практикуйте його у дні без інцидентів, коли можна думати.
  3. Виправляйте патерни роботи з файлами перед тим, як «фіксити» файлові системи: менше файлів, менше stat, менше fsync, розумніша агрегація.
  4. Зробіть топологію і розміщення явними для великих задач; не залишайте це на рулетку планувальника.
  5. Агресивно виводьте підозрілі вузли; один ненадійний NIC може перетворити «масштабування» на «страждання».

Масштабувати проблеми до Місяця — це вражає. Залишатися надійним завдяки нудній правильності — ось як тримати систему в роботі.

← Попередня
Теплова стіна: як фізика поклала край улюбленій маркетинговій історії
Наступна →
Рейтрейсинг: що він дає крім красивих скриншотів

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