Ви бачили це: закупівля бере «найшвидші» CPU, бо на специфікації написано 3.8 GHz. Сервіс усе одно повзає, p95 вибухає, і на-call дізнається різницю між маркетингом і фізикою о 03:17.
Якщо ви запам’ятаєте з цього матеріалу одне речення, нехай це буде: GHz — це як часто тикає метроном. IPC — скільки роботи робиться за один такт. Ваше навантаження живе у частині «робота».
Коротка модель в одній фразі
Продуктивність ≈ (частота) × (IPC) × (кількість корисних ядер) − (очікування на пам’ять, I/O, блокування та інших людей).
GHz — це один множник. IPC — інший. А від’ємний доданок — очікування — саме тому ваше «оновлення до 3.8 GHz» може нічого не змінити, крім підігріву кімнати. Ви не зможете обігнати промах кеша, шторм page fault-ів або конвой блокувань високою частотою. Але ви можете діагностувати, за що саме ви платите.
Що таке IPC насправді (і чого це не означає)
IPC — це instructions per cycle: в середньому, скільки інструкцій CPU завершує (retire) за один такт. Якщо ядро працює на 3.5 GHz, це 3.5 мільярда тактів за секунду. Якщо IPC = 2.0, воно завершує приблизно 7 мільярдів інструкцій на секунду (приблизно; у реальності є нюанси).
Завершені інструкції: відро «зроблено»
Сучасні CPU — це фабрики з out-of-order виконанням. Вони фетчать, декодують і спекулюють. Вони виконують інструкції паралельно, перемішують порядок і іноді викидають роботу, якщо спекуляція була хибною. IPC зазвичай відноситься до retired інструкцій: інструкції, що пройшли конвеєр і стали архітектурним фактом.
IPC не означає «наскільки розумний CPU»
IPC залежить від навантаження. Той самий CPU може показувати високий IPC для щільних обчислюваних циклів і жахливий IPC для pointer-chasing у пам’яті. Інший CPU може поводитися інакше. Тому бенчмаркування одного мікросервісу і екстраполяція на «усю платформу» часто приводить до регретів.
Практичний образ
Уявіть ядро CPU як кухню ресторану:
- GHz — як часто головний шеф плескає, сигналізуючи «наступний крок».
- IPC — скільки страв виходить з паса за одне плескання.
- Затримка кешу/пам’яті — час, витрачений на очікування інгредієнтів.
- Branch mispredicts — коли шеф готує не ту страву і її викидають.
- Locks/конкуренція — коли кухня бореться за одну сковорідку, яку всі потребують.
Плескати частіше не допоможе, якщо ви все ще чекаєте вантажівку з помідорами.
Жарт №1: Купувати CPU за GHz — як наймати барменів за швидкість трясіння порожнього шейкера.
Чому GHz постійно вас вводить в оману
Тактова частота легко поміщається на коробці. Це також найменш стабільна величина в сучасному сервері, бо частота — це результат переговорів між лімітом потужності, тепловими обмеженнями, правилами turbo і тим, «що ще відбувається на цьому сокеті зараз».
Turbo — умовний, а не спосіб життя
Рекламований «max turbo» зазвичай:
- для одного або кількох ядер,
- на обмежений проміжок часу,
- при певних умовах енергоспоживання і температури,
- за навантаження, яке не викликає зниження частоти (наприклад, AVX-важкий код на деяких архітектурах).
Якщо ваш сервіс використовує всі ядра під навантаженням (більшість так і робить), вам важлива підтримувана частота при всіх ядрах, а не пікова.
Частота може падати, коли робота «важча»
Деякі набори інструкцій споживають більше потужності. Векторний код може знижувати частоти. Також це може статися через перегрів у щільних стійках або через погані налаштування BIOS. GHz — не фіксована властивість; це наслідок.
Навіть при високому GHz IPC може бути низьким
Низький IPC виникає, коли ядро витрачає цикли не на завершення інструкцій: очікує на промахи кеша, відновлюється після неправильної передбачуваної гілки, стоїть через залежності або обмежено обмеженнями front-end (не вистачає фетчу/декоду).
Одна діаграма, яку варто тримати в голові
Уявіть часову шкалу CPU:
- Retiring (добре): інструкції завершуються.
- Front-end bound: недостатньо інструкцій подається в виконання.
- Back-end bound: вузьке місце — блоки виконання або підсистема пам’яті.
- Bad speculation: робота викидається через неправильну спекуляцію.
Високий GHz лише прискорює часову шкалу. Він не змінює, який сегмент домінує.
Чотири речі, що контролюють IPC
Є багато мікроархітектурних деталей, але ви можете тримати IPC під контролем за допомогою чотирьох великих важелів, що проявляються в продукції.
1) Ієрархія пам’яті: хіти кешу — це зарплатня
Промах кеша — швидко; DRAM — повільно; сховище — крижані віки. Ваш IPC падає, коли CPU чекає дані. Найогидніші випадки — pointer-chasing навантаження (hash tables, B-trees, об’єктні графи), де кожен крок залежить від попереднього завантаження.
Для багатьох серверних кодів важливіша латентність, ніж пропускна здатність. Якщо доступ до даних послідовний, ви не «використовуєте всю ту смугу пам’яті». Ви чекаєте на один ланцюжок промахів.
2) Передбачення гілок: CPU вгадує ваше майбутнє
CPU спекулює. Якщо предиктор вгадує правильно, ви виглядаєте генієм. Якщо він помиляється — конвеєр скидається і ви платите циклами. Код з великою кількістю гілок і непередбачуваними патернами може поховати IPC, навіть якщо дані гарячі в кеші.
3) Instruction-level parallelism (ILP): скільки може відбуватися одночасно
Out-of-order ядра можуть виконувати кілька операцій за такт, якщо є незалежні інструкції. Щільні цикли з залежностями (наприклад, ітеративне хешування, де кожен крок залежить від попереднього) обмежують ILP. Ви побачите низький IPC навіть коли все в кеші.
4) Front-end: фетч/декод має свої вузькі місця
Якщо CPU не може подати інструкції в execution engine достатньо швидко — через I-cache промахи, погане розташування коду або обмеження декоду — IPC падає. Це проявиться у великих бінарях з поганою локальністю, JIT-коді з частою інвалидацією або навантаженнях із частими інвалідизаціями інструкційного кешу.
Правило великого пальця: Якщо ви не знаєте вузьке місце, припускайте, що це латентність пам’яті або конкуренція. Ці два пункти платять мою іпотеку.
IPC і реальні навантаження: БД, веб, сховище, JVM
Бази даних: «CPU-bound» часто означає «чекає на пам’ять, тримаючи блокування»
Бази даних можуть бути обчислювально важкими (стиснення, шифрування, виконання запитів), але часто домінують патерни доступу до пам’яті: пошуки по індексу, коливання buffer pool і структури з великою кількістю вказівників. Сервер БД може показувати 40–70% завантаження CPU і все одно бути «обмеженим CPU», бо гарячі потоки стоять, а решта — проста або заблокована.
Що робити: виміряйте stalls, LLC misses і час у блокуваннях. Якщо ви збільшуєте GHz без зменшення частоти промахів кеша, ви просто змушуєте CPU ще швидше чекати.
Веб-сервіси: гілки, алокації і хвостова латентність
Обробники запитів багаті на гілки: перевірки автентифікації, маршрутизація, feature flags, серіалізація. Пенальті за mispredict проявляються як варіація хвостової латентності. Додайте тиск GC — і у вас є CPU-час, що не завершує корисних інструкцій.
Що робити: шукати mispredicts і проблеми інструкційного кешу, зменшити алокації. Якщо доводиться купувати залізо, надавайте перевагу ядрам з сильною пер-потоком продуктивністю та доброю поведінкою кеша, а не найвищому рекламованому turbo.
Шляхи зберігання/IO: IPC — жертва, а не причина
Шляхи зберігання часто вузькі через переривання, syscall-и, переключення контексту і конкуренцію блокувань у ядрі або драйвері. CPU може витрачати цикли в kernel mode, не завершуючи багато прикладної роботи. Метрики IPC можуть виглядати дивно, бо ви вимірюєте не ту «корисну роботу».
Що робити: вимірюйте iowait, softirq time і rate викликів syscall. Не «оптимізуйте», прирівнюючи все до одного ядра, якщо вам подобаються сюрпризи.
JVM і керовані рантайми: JIT може підвищити IPC… поки не перестане
JIT-компіляція може продукувати щільні цикли з гарною локальністю і високим IPC. Водночас вона може створювати мегаморфні виклики, деоптимізовані шляхи та часті safepoint-и, які перетворюють CPU на планувальник. При діагностиці відділяйте «CPU витрачено на роботу» від «CPU витрачено на управління рантаймом».
Цитата, щоб бути чесним: «Надія — не стратегія.» — General Gordon R. Sullivan
Факти й історія для аргументів
- Тактові частоти вперлися у стелю в середині 2000-х через щільність потужності і витік струму; індустрія перейшла від «швидших частот» до «більше ядер» і «більше роботи за такт».
- «NetBurst» (ера Pentium 4) гналася за високими GHz з глибокими конвеєрами; часто програвала менш тактовим дизайнам з кращим IPC, особливо на реальному коді.
- Out-of-order виконання існує переважно щоб покращувати IPC шляхом використання ILP; це апаратний планувальник, що працює на GHz.
- Branch predictors стали складними, бо mispredict-и дорогі; глибші конвеєри зробили помилкові вгадування ще болючішими.
- Кеші CPU зростали, бо DRAM не встигала; «memory wall» — повторювана тема в архітектурі.
- IPC не є порівнянним між ISA; порівнювати IPC між x86 і ARM без контексту зазвичай безглуздо, бо «інструкція» — не універсальна одиниця роботи.
- Спекуляція й безпека зіткнулися наприкінці 2010-х (клас Spectre); деякі міри зменшили продуктивність через обмеження спекуляції й збільшення затрат на доступ до пам’яті.
- Performance counters існують десятиліттями, але «observability для заліза» стала масовою в ops, коли ефективність флоту й хмарні витрати змусили краще вимірюватися.
Швидкий план діагностики
Це робочий процес «я маю 30 хвилин до інцидентного дзвінка». Мета — не дисертація. Мета — визначити домінантний вузький прохід і ухвалити безпечне рішення.
Перше: підтвердіть, який тип «повільності» у вас
- Латентність зросла, але CPU низький? Ймовірно очікування: I/O, блокування, мережа або memory stalls.
- CPU на максимумі і пропускна здатність зафіксована? Може бути compute-bound, але також могуть бути spinlocks, syscall-шторм або GC.
- Тільки хвостова латентність погана? Шукайте конкуренцію, GC-паузи, шумних сусідів, троттлінг та thrash кеша.
Друге: перевірте частоту і тротлінг
- Чи CPU працює на очікуваних підтримуваних частотах?
- Чи в дії обмеження потужності (PL1/PL2), термальне тротлінг або cgroup-квоти?
Третє: виміряйте, чи ви завершуєте роботу
- Подивіться на IPC і stalls за допомогою performance counters.
- Якщо IPC низький і stalls високі: ймовірні причини — пам’ять/гілки/блокування.
Четверте: вирішіть, яка підсистема володіє інцидентом
- Високий iowait / заблоковані задачі → шлях зберігання/мережі.
- Висока черга runnable і багато context switch → планування CPU/oversubscription.
- Високий рівень промахів кеша → локальність даних, робочий набір або розміщення NUMA.
- Високий рівень промахів гілок → непередбачуваність шляхів коду або погане розташування коду.
П’яте: оберіть одну дію, яку ви можете обґрунтувати
Приклади: зменшити конкуренцію, перемістити шумну пакетну роботу, відрегулювати CPU-квоти, прикупити пам’ять до локального NUMA або відкотити зміни, що збільшили робочий набір.
Практичні завдання: команди, виводи, рішення
Це реальні завдання, які ви можете виконати на Linux. Кожне містить, що означає вивід і яке рішення з нього випливає. Запускайте їх на ураженому хості, а не на вашому ноутбуці. Реальність вороже ставиться до теорії.
Завдання 1: Подивіться модель CPU і рекламовані base/max
cr0x@server:~$ lscpu | egrep 'Model name|CPU MHz|CPU max MHz|CPU min MHz|Socket|Core|Thread|NUMA'
Model name: Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz
CPU MHz: 2095.123
CPU max MHz: 3900.0000
CPU min MHz: 800.0000
Socket(s): 2
Core(s) per socket: 20
Thread(s) per core: 2
NUMA node(s): 2
Значення: На коробці вказано 3.9 GHz max, але зараз ~2.1 GHz. Це може бути нормально (base clock), або це може бути тротлінг під навантаженням.
Рішення: Якщо скарги на продуктивність співпадають з низьким current MHz під високим навантаженням, переходьте до перевірки governor і тротлінгу.
Завдання 2: Спостерігайте реальні частоти по ядрах
cr0x@server:~$ sudo turbostat --quiet --interval 1 --num_iterations 5
CPU Avg_MHz Busy% Bzy_MHz IRQ
- 2240 62.14 3605 1123
Значення: Avg_MHz — середнє по всіх ядрах; Bzy_MHz — частота під час завантаження. Тут зайняті ядра підскакують до ~3.6 GHz.
Рішення: Якщо Bzy_MHz набагато нижче очікуваного (наприклад, застрягло близько 2.1 GHz), підозрюйте обмеження потужності/термальний тротлінг, налаштування BIOS або AVX downclock.
Завдання 3: Перевірте CPU governor (поширено в старих конфігураціях і деяких cloud image)
cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
powersave
Значення: Кернел намагається економити енергію, що може знизити чутливість частоти.
Рішення: На серверах з чутливістю до затримки встановіть performance (після підтвердження політики). Якщо ви в датацентрі з обмеженням потужності, координуйтеся; не дивуйте Facilities.
Завдання 4: Перевірте cgroup CPU throttling (контейнери чемно брешуть)
cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 983211234
user_usec 702112345
system_usec 281098889
nr_periods 12345
nr_throttled 9321
throttled_usec 551234567
Значення: nr_throttled і throttled_usec показують, що робоче навантаження часто тротлиться через CPU-квоти.
Рішення: Якщо тротлінг істотний у вікні інциденту, збільшіть CPU-квоту/запити або зменшіть конкуренцію. Купівля більшого GHz не виправить квоту.
Завдання 5: Швидка «CPU чи очікування?» знімка
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 st
6 0 0 812344 22044 912344 0 0 1 3 1800 4200 32 8 58 1 0
12 0 0 809112 22044 913008 0 0 0 0 2100 5200 44 11 45 0 0
Значення: r — runnable threads, b — blocked, розподіл CPU на user/system/idle/iowait. Низьке wa говорить, що, ймовірно, немає проблем зі сховищем.
Рішення: Якщо r постійно вищий за кількість CPU і id низький, ви CPU-сатуровані або сильно контендитесь. Перейдіть до вимірювання IPC/stalls.
Завдання 6: Перевірте run queue і load відносно ядер
cr0x@server:~$ uptime
10:41:22 up 23 days, 4:12, 2 users, load average: 38.12, 41.03, 39.77
Значення: Load average близький або вище за кількість ядер — багато runnable або uninterruptible задач.
Рішення: Якщо load високий, але CPU є проста, підозрюйте заблокований I/O або mutex contention. Поєднайте з vmstat і pidstat.
Завдання 7: Визначте гарячі процеси і чи час у user vs kernel
cr0x@server:~$ pidstat -u -p ALL 1 3
Linux 6.2.0 (server) 01/10/2026 _x86_64_ (80 CPU)
# Time UID PID %usr %system %CPU Command
10:41:40 1001 21433 520.00 80.00 600.00 java
10:41:40 0 1321 10.00 120.00 130.00 ksoftirqd/12
Значення: JVM використовує багато user CPU; ksoftirqd вказує на інтенсивну обробку soft interrupt-ів (часто мережа).
Рішення: Якщо ksoftirqd гарячий, дивіться на швидкість мережевих пакетів, розподіл IRQ і налаштування NIC. Якщо домінує user CPU — дивіться IPC і шляхи коду.
Завдання 8: Швидко виміряти IPC з perf (системно)
cr0x@server:~$ sudo perf stat -a -e cycles,instructions,branches,branch-misses,cache-references,cache-misses -- sleep 10
Performance counter stats for 'system wide':
38,221,456,789 cycles
41,002,112,334 instructions # 1.07 insn per cycle
7,991,122,010 branches
211,334,556 branch-misses # 2.64% of all branches
2,114,334,221 cache-references
322,114,990 cache-misses # 15.23% of all cache refs
10.001234567 seconds time elapsed
Значення: IPC ≈1.07 по системі. Рівень промахів гілок ~2.6% (не катастрофа). Частка промахів кеша помітна.
Рішення: Низький IPC плюс значущі промахи кеша вказують на проблеми локальності даних/робочого набору. Не ганяйтеся за GHz; працюйте над промахами, розкладкою даних, NUMA і контенцією.
Завдання 9: Виміряти IPC для одного процесу (корисно під час інциденту)
cr0x@server:~$ sudo perf stat -p 21433 -e cycles,instructions -- sleep 10
Performance counter stats for process id '21433':
12,112,334,990 cycles
10,001,223,110 instructions # 0.83 insn per cycle
10.000998321 seconds time elapsed
Значення: Цей процес завершує <1 інструкції за такт у середньому. Це «багато очікувань» або «багато важкого для паралелізації коду».
Рішення: Якщо додаток має бути compute-bound, це вказує на stalls (пам’ять, блокування, syscalls, гілки). Продовжуйте top-down аналіз і flame graphs, якщо дозволено.
Завдання 10: Помітити великі faults і page-fault шторм (вони вбивають IPC)
cr0x@server:~$ pidstat -r -p 21433 1 3
# Time UID PID minflt/s majflt/s VSZ RSS %MEM Command
10:42:20 1001 21433 12000.00 0.00 18432324 9321120 28.3 java
10:42:21 1001 21433 11500.00 3.00 18432324 9325540 28.3 java
Значення: Великі minor faults можуть бути нормою (наприклад, memory-mapped файли), але major faults означають page-in з диска. Це латентність, яка стає зупинками CPU.
Рішення: Якщо major faults ростуть під час піків латентності, досліджуйте тиск пам’яті, поведінку THP і churn файлового кешу; додайте RAM або зменшіть робочий набір.
Завдання 11: Перевірте баланс NUMA і доступ до віддаленої пам’яті (прихована латентність)
cr0x@server:~$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0-39
node 0 size: 192000 MB
node 0 free: 81234 MB
node 1 cpus: 40-79
node 1 size: 192000 MB
node 1 free: 23456 MB
Значення: Два NUMA-ноди; node 1 набагато більше завантажений. Дисбаланс може означати віддалені доступи до пам’яті.
Рішення: Якщо процес займає сокети, але пам’ять не локальна, прив’яжіть CPU/пам’ять або виправте розміщення планувальника. Віддалена пам’ять — податок, який ви платите в IPC.
Завдання 12: Підтвердити, чи THP спричиняє варіацію латентності
cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
Значення: THP встановлено в always. Це може допомогти пропускній здатності, але іноді шкодить хвостовій латентності через паузи компакції.
Рішення: Для баз даних з чутливими латентностями розгляньте madvise або never після тестування. Змінюйте обережно; це політика, а не магія.
Завдання 13: Перевірте CPU steal time (реальність хмари)
cr0x@server:~$ mpstat 1 3
Linux 6.2.0 (server) 01/10/2026 _x86_64_ (8 CPU)
10:43:10 AM all %usr %nice %sys %iowait %irq %soft %steal %idle
10:43:11 AM all 41.00 0.00 9.00 0.00 0.00 1.00 8.00 41.00
Значення: %steal показує час, коли ваша VM хотіла CPU, але гіпервізор не запланував вас. Це може виглядати як «низький IPC», бо ви просто не працюєте.
Рішення: Якщо steal високий під час інцидентів, змініть тип інстансу, уникайте пересаджених хостів або домовтеся про розміщення. Не переробляйте код, щоб виправити шумного сусіда.
Завдання 14: Швидко перевірте латентність диску (бо сховище маскується під CPU)
cr0x@server:~$ iostat -x 1 3
Linux 6.2.0 (server) 01/10/2026 _x86_64_ (80 CPU)
Device r/s w/s r_await w_await aqu-sz %util
nvme0n1 120.0 450.0 2.10 8.40 4.20 96.00
Значення: Високе %util і підвищений await вказують, що пристрій насичений або чергується. Це означає, що потоки блокуються, CPU іде в проста або відбуваються контекст-скіки, і ваша «мрія про апгрейд CPU» тихо помирає.
Рішення: Якщо await диска збігається з піками латентності, виправляйте I/O: зменште синхронні записи, налаштуйте черги, додайте пристрої або розділіть навантаження.
Завдання 15: Підтвердити розподіл переривань (IRQ-шторм зменшує корисний IPC)
cr0x@server:~$ grep -E 'eth0|nvme' /proc/interrupts | head
64: 99112233 0 0 0 IR-PCI-MSI 524288-edge eth0-TxRx-0
65: 112233 0 0 0 IR-PCI-MSI 524289-edge eth0-TxRx-1
66: 99887 0 0 0 IR-PCI-MSI 524290-edge eth0-TxRx-2
Значення: IRQ-и падають на одне CPU (перший стовпець), тоді як інші показують нуль. Це гарячий плям, часто викликає softirq load і латентність.
Рішення: Виправте IRQ affinity / RPS/XPS політику і забезпечте розподіл черг NIC. Якщо лишити як є, ви масштабуватимете ядра і все одно вперетеся в CPU0.
Завдання 16: Перевірте баланс пропускної здатності пам’яті vs тиск латентності (швидка евристика)
cr0x@server:~$ sudo perf stat -a -e cpu/mem-loads/,cpu/mem-stores/ -- sleep 5
Performance counter stats for 'system wide':
882,112,334 cpu/mem-loads/
401,122,110 cpu/mem-stores/
5.000912345 seconds time elapsed
Значення: Високі counts load/store можуть вказувати на memory-intensive поведінку; поєднайте з метриками cache-miss, щоб зрозуміти, чи це в основному хіти кеша або доступ до віддалої/DRAM.
Рішення: Якщо memory ops високі і IPC низький, ймовірно у вас memory-bound сервіс. Віддавайте перевагу CPU з більшими кешами, кращим prefetch і підсистемою пам’яті, а не лише MHz.
Три міні-історії з корпоративного життя
1) Інцидент через хибне припущення: «Вищий GHz = швидший API»
Середня SaaS-компанія мала API оформлення замовлення, що гальмував під час маркетингових кампаній. Інженери помітили насичення CPU на апп-тіру. Закупівля підготувала план: апгрейд до «швидших CPU» з вищою рекламованою частотою. Міграція була запланована, дашборди погоджені, і всі чекали тріумфу.
Після cutover середнє завантаження CPU трохи впало. Латентність не змінилася. Хвостова латентність навіть погіршилася. Постмортем інциденту почався з незручного слайду: «Ми купили 3.9 GHz частини, чому ми повільніші?»
Зрештою вони виміряли IPC і промахи кеша. Нові CPU мали вищі turbo-частоти, але менший last-level кеш на ядро і іншу топологію пам’яті. Їхній сервіс домінував завдяки stalls через промахи кеша від великого in-memory каталогу товарів та купи feature flags. IPC впав під навантаженням, бо робочий набір частіше вилітав з кеша, і підсистема пам’яті працювала інтенсивніше.
Виправлення було немодним: зменшити робочий набір (видалити мертві flags, стиснути гарячі структури даних), покращити локальність (зберігати ID підряд, уникати pointer-heavy мап), і закріпити найгарячіші процеси за локальною NUMA-пам’яттю. Залізо не було «поганим». Припущення було таким.
Вони залишили машини, але перенаправили їх на batch-завдання, де частота допомагала. Флот checkout перейшов на CPU з більшим кешем і кращою підтримкою sustain all-core, а не на найвищий turbo-стікер.
2) Оптимізація, що відкотилася: «Закрепімо все для кращої локальності кеша»
Інша організація мала latency-sensitive сервіс ingest логів. Хтось помітив, що міграцій CPU багато під піком. Вирішили агресивно прив’язати основні треди ingest до підмножини ядер через cpusets, сподіваючись покращити локальність кеша і зменшити контекстні переключення.
День графіки виглядали краще: менше міграцій, трохи нижча середня латентність. Потім змінився трафік. Новий клієнт прислав більші payload-и, що збільшило парсинг і алокації пам’яті. Закріплені ядра дійшли до 100% завантаження, тоді як решта системи залишалася недовантаженою. Хвостова латентність виросла, черга збільшилася.
Корінь проблеми не був у «недостатньому GHz». Це була самонанесена зірваність планування. Pinning забрав у ядра можливість розкидати навантаження, а також концентрував переривання і роботу ядра на тих же ядрах. IPC на закріплених ядрах впав, бо вони проводили більше часу в kernel-шляхах і очікуванні пам’яті через більші payload-и.
Виправлення — прив’язувати лише те, що потребує pinning (кілька чутливих тредів), правильно розподілити IRQ і залишити запас для варіативності. Також ввели backpressure і обмежили concurrency на підключення, щоб уникнути перетворення stalls в колапс черг.
Жарт №2: Закріплення CPU — як татуювання свого місця в аеропорту: відчуваєш себе в безпеці, поки літак не змінить воротa.
3) Нудна, але правильна практика, що врятувала день: «Виміряй перед тим, як торкатися»
Фінтех-команда вела OLTP кластер з жорсткими SLOs по латентності. Під час квартального навантажувального тесту p99 зріс на 40% на одному шарді. Першою думкою було масштабувати інстанс і забути. Натомість on-call пройшов нудний ранбук: захопити базові лічильники, підтвердити частоту, перевірити тротлінг, валідувати NUMA локальність, потім інспектувати I/O.
Вони виявили: частота CPU в порядку. IPC в порядку. Промахи кеша стабільні. Але iowait різко виріс у тому ж вікні. iostat показав підвищену write-latency і велике завантаження пристрою. Шард приземлився на хості, де інше шумне навантаження насичувало той самий NVMe. Це не було проблемою CPU.
«Нудна» практика була проста: обов’язкове захоплення perf stat, vmstat і iostat знімків під час інцидентів з таймстемпами. Це зробило шаблон очевидним і запобігло дорогому і неправильному апгрейду CPU.
Вони вирішили проблему, ізолювавши сховище для БД і відрегулювавши планування, щоб шумні сусіди не ділили пристрої. Навантажувальний тест пройшов на існуючому класі CPU. Ніхто не отримав трофей, але SLO залишився зеленим — а це єдиний важливий трофей.
Поширені помилки
1) «CPU на 60%, отже ми не CPU-bound»
Симптом: Латентність висока, CPU не зашкалює, потоки накопичуються.
Корінь: Декілька гарячих тредів стоять на пам’яті/блокуваннях, тоді як інші ядра прості; загальне CPU приховує проблеми по тредах.
Виправлення: Використайте профайлінг по тредах і perf counters; зменште contention; покращіть локальність даних; обмежте concurrency, щоб уникнути черг.
2) «Нам потрібен вищий GHz»
Симптом: Пропускна здатність зафіксована; закупівля радить «швидші тактові SKU».
Корінь: IPC низький через промахи кеша, mispredict-и, syscalls або overhead рантайму. Частота не є обмежувальним множником.
Виправлення: Виміряйте IPC і рівні misses; оптимізуйте гарячі шляхи; обирайте CPU з кешем і пам’яттю, що відповідають навантаженню.
3) «Turbo каже 4.2 GHz, отже у нас 4.2 GHz»
Симптом: Бенчмарки стрибкоподібно різні між прогоном; продакшн повільніший за дев-тести.
Корінь: Turbo — оппортунистичний; підтримувана частота при всіх ядрах нижча під реальним навантаженням; правила потужності/тепла змінюють поведінку.
Виправлення: Виміряйте Bzy_MHz під репрезентативним навантаженням; перевірте BIOS power limits; забезпечте охолодження; уникайте оцінювання CPU по single-thread мікротестам лише.
4) «Ми оптимізували pinning тредів, тепер гірше»
Симптом: Середня латентність покращилась, але хвостова латентність і беклог погіршилися після зміни трафіку.
Корінь: Надмірне pinning зменшило гнучкість планувальника і сконцентрувало переривання; локальні оптимізації створили глобальну нестачу ресурсів.
Виправлення: Pinning у міру; розподіліть IRQ; залиште вільні ядра; валідуйте на різних профілях трафіку.
5) «Наш CPU повільний в хмарі»
Симптом: CPU виглядає завантаженим, але вихід роботи низький; періодичні стрибки латентності.
Корінь: Steal time, тротлінг або шумні сусіди; ви фактично не працюєте тоді, коли думаєте.
Виправлення: Перевірте %steal і cgroup throttle stats; обирайте інші instance families; використовуйте dedicated hosts за потреби.
6) «IPC низький, отже CPU поганий»
Симптом: perf показує <1 IPC і паніка панує.
Корінь: Навантаження memory-latency bound (pointer chasing), або вимір охоплює системний kernel time, не пов’язаний з прогресом додатка.
Виправлення: Виміряйте по-процесу, корелюйте з промахами кеша і деталізацією stalls; подумайте про зміни алгоритмів/структур даних перед тим, як звинувачувати кремній.
7) «Ми апгрейдили CPU і стало повільніше»
Симптом: Регресії на новому апаратному забезпеченні під реальним навантаженням.
Корінь: Інші розміри кеша, топологія NUMA, швидкості пам’яті або мікроархітектурна поведінка; також можуть змінитися дефолтні налаштування BIOS.
Виправлення: Перетюнте NUMA, налаштування BIOS і параметри ядра; перебазуйте perf counters; валідуйте з продакшн-подібним навантаженням.
Контрольні списки / покроковий план
При виборі CPU (або типу інстансу): що робити і чого уникати
- Класифікуйте навантаження: compute-bound, memory-latency-bound, memory-bandwidth-bound, I/O-bound або contention-bound. Якщо не знаєте — виміряйте.
- Використовуйте репрезентативні бенчмарки: ваш реальний сервіс, реальні форми даних, реальна конкуренція, реальні налаштування GC. Синтетика підходить для sanity, але не для закупівлі.
- Порівнюйте sustain all-core поведінку: вимірюйте під навантаженням 10–30 хвилин, а не 30 секунд пікового turbo.
- Перевірте кеш на ядро: особливо для in-memory сервісів і баз даних. Кеш часто дає більший ефект, ніж +300 MHz.
- Валідуйте NUMA fit: чи вміщується робочий набір в один сокет? Якщо ні — плануйте локальність пам’яті і витрати на інтерконект.
- Рахуйтеся з наслідками безпекових міграцій: переконайтесь, що порівняння відбуваються в однаковому kernel і microcode стані.
- Шукайте ризики тротлінгу: power capping, щільні стійки, cloud CPU credits, cgroup квоти.
- Уникайте покупки по піковому GHz: віддавайте перевагу опублікованій per-core продуктивності при sustain для ваших власних IPC/сounter baseline-ів.
Під час інциденту: мінімальний безпечний робочий процес
- Захопіть
vmstat 1 5,mpstat 1 3,iostat -x 1 3. - Перевірте тротлінг: cgroups, частоту і steal time.
- Швидко виміряйте IPC з
perf stat(системно і для гарячого PID). - Вирішіть: compute vs memory stalls vs I/O vs contention. Виберіть одне.
- Зробіть одну оборотну дію: зменшити concurrency, перемістити шумне навантаження, відрегулювати квоту або відкотити зміни, що розширили робочий набір.
- Запишіть лічильники і часовий інтервал. Майбутньому собі це знадобиться.
Після інциденту: зробіть наступний раз дешевшим
- Створіть базовий дашборд: IPC proxy (instructions/cycle), промахи кеша, context switches, major faults, iowait, steal time.
- Кодифікуйте ранбук з командами і «що вважати хорошим».
- Раз на квартал запускайте «hardware reality» тест: sustain clocks, терміки і перевірки тротлінгу під навантаженням.
- Навчіть procurement одній фразі: «sustained all-core performance with workload counters». Повторюйте, доки не отримаєте фінансування.
Питання та відповіді
1) Який «добрий» показник IPC?
Залежить від CPU і навантаження. Багато реальних сервісів працюють приблизно на ~0.5–2.0 IPC. Щільні compute-цикли можуть мати вищі значення. Pointer-chasing може мати значно нижчі. Тренд важливіший: якщо IPC впав після деплою — щось змінилося в поведінці (робочий набір, гілки, syscalls, блокування).
2) Якщо IPC низький, чи потребую кращого CPU?
Зазвичай ні. Низький IPC часто означає, що ви чекаєте на пам’ять, I/O або конкуренцію. «Кращий CPU» допоможе, якщо має потужнішу підсистему пам’яті, більші кеші або кращий branch prediction для вашого коду. Але перша перемога зазвичай програмна: зменшити промахи, зменшити блокування, зменшити алокації.
3) Чому бенчмарки показують великі виграші від вищого GHz, а в продакшні ні?
Бенчмарки часто вміщуються в кеш, працюють single-threaded і уникають I/O та конкуренції. Продакшн має більші робочі набори, більше гілок, більше syscall-ів, більше переривань і сусідів. GHz допомагає, коли CPU є обмежувальним фактором, а не коли він чекає.
4) Більше ядер переможе вищий IPC?
Лише якщо ваше навантаження масштабується лінійно. Багато сервісів обмежені спільними ресурсами: locks, database connections, memory bandwidth або одним чергою. Якщо масштабування зупиняється на 16 тредах, купівля 64 ядер не виправить ситуацію. Сильніший per-core (вищий IPC) може бути кращою інвестицією.
5) Чи порівнюваний IPC між Intel і AMD? Між x86 і ARM?
Між CPU тієї ж ISA і епохи порівняння IPC можуть бути корисними. Між різними ISA — це слабша ідея, бо набір інструкцій відрізняється. Використовуйте енд-ту-енд throughput/latency бенчмарки і лічильники, щоб зрозуміти чому, замість того щоб ставити IPC як універсальний бал.
6) Як розмір кеша впливає на IPC?
Більше кешу може покращити IPC, переводячи доступи до DRAM у хіти кеша. Але це не лише розмір: латентність, асоціативність, поведінка prefetch і топологія core-to-cache важливі. Для багатьох серверних навантажень last-level кеш на ядро — критичний параметр.
7) Чи може деплой змінити IPC без великої зміни використання CPU?
Так. Ви можете тримати CPU на 60% і все одно отримати повільніший сервіс, якщо нова версія збільшує робочий набір, викликає більше промахів кеша, додає гілки, підвищує syscalls або вводить блокування. CPU utilization — грубий інструмент; IPC і індикатори stalls покажуть, що саме досягнуто за цей час CPU.
8) Чи правильний висновок «GHz не важливий»?
Ні. GHz важливий, коли ви дійсно compute-bound і ефективно завершуєте інструкції. Суть у тому, що GHz недостатній і часто нестабільний. Розглядайте частоту як один із входів і валідуйте її лічильниками і реальними тестами навантаження.
9) Який найшвидший спосіб пояснити IPC стейкхолдеру неінженеру?
«Частота — це як часто тикає процесор. IPC — скільки роботи він робить за один тик. Наш сервіс обмежений тим, скільки він чекає, а не тим, як часто тикати.»
Висновок: наступні кроки, які ви дійсно виконаєте
Якщо ви зміните лише одну звичку: перестаньте вважати GHz гарантією продуктивності. Почніть ставитися до нього як до змінної.
Зробіть це далі:
- Виберіть один критичний сервіс і захопіть базові показники під типовим навантаженням:
turbostat,perf stat,vmstat,iostatі cgroup throttle stats. - Запишіть три числа, що мають значення: sustained busy MHz, IPC і співвідношення промахів кеша. Тепер у вас є відбиток.
- Коли продуктивність регресує, порівняйте відбитки перед тим, як сперечатися про залізо. Якщо IPC впав — шукайте очікування. Якщо MHz впав — шукайте тротлінг. Якщо обидва в порядку — дивіться далі (I/O, блокування, мережа).
- Коли треба купувати залізо, купуйте під вузьке місце, яке ви виміряли: кеш і пам’ять для memory-bound сервісів; sustained frequency для compute-bound; і передбачуване планування для latency-sensitive навантажень.
Вам не потрібно ставати CPU-архітектором. Вам просто потрібно перестати дивуватися тим самим вузьким місцям знову й знову. IPC — найшвидший спосіб перестати даватися обдуреним великим числом GHz дрібним шрифтом.