Hyper-Threading розкрито: магічні потоки чи хитрощі планувальника?

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

На вашій панелі видно «CPU 40%», але p99 латентність наче зірвалася зі сходів. Хтось пробурмотів «Hyper-Threading», інший сказав «SMT», і раптом ви на нараді про відключення половини CPU, за які заплатили.

Hyper-Threading (бренд Intel для simultaneous multithreading, SMT) — не магія і не шахрайство. Це компроміс: вищий сумарний пропуск у обмін на спільні мікроархітектурні ресурси, складніший планувальник і реальні режими відмов. Якщо ви керуєте продакшеном, треба знати, коли SMT — ваш друг, коли це шумний співмешканець, і як довести це цифрами.

Що таке Hyper-Threading насправді (і чого він не є)

Ядро CPU має конвеєри, виконавчі блоки, кеші, буфери, предиктори переходів, черги завантаження/запису та купу облікових структур, що тримають інструкції в русі. Багато цієї машини стоїть без діла, коли потік чекає — часто через пам’ять, іноді через переходи, іноді через I/O або через ризики конвеєра.

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

SMT не подвоює ваш обчислювальний потенціал. Ви не отримуєте два ALU, два L1 кеші, два FP-блоки або два контролери пам’яті. Ви отримуєте два архітектурні контексти (набори регістрів і деякий облік на потік) і логіку для переміжного випуску інструкцій. Прискорення залежить від навантаження, часто скромне, іноді негативне.

«Але ОС показує вдвічі більше CPU.» Так. ОС бачить логічні CPU. Це проблема назви: люди чують «CPU» і припускають «незалежний обчислювальний двигун». Логічний CPU часто є лише ціллю планувальника, а не гарантією продуктивності.

Якщо потрібна ментальна модель: SMT — це як дозволити двом автомобілям ділити одну смугу з хитрим злиттям «зіппер». Чудово працює при спалахах трафіку. Жахливо, коли обидва водії вирішують їхати поруч, як гонщики.

Жарт №1: Hyper-Threading — це як додати другий кермовий механізм у машину. Двигун не подвоюється, але сварок стає вдвічі більше.

Факти & історія, що пояснюють поведінку сьогодні

Кілька конкретних пунктів, які допомагають заякорити міфи в реальності:

  1. SMT передувало бренду «Hyper-Threading». IBM впроваджувала SMT у висококласних POWER-системах ще до того, як це стало популярним в x86-середовищі.
  2. Intel комерційно представив Hyper-Threading в епоху Pentium 4. Воно допомагало ховати довгі конвеєрні затримки, але дизайн P4 також робив продуктивність «стрибкоподібною» й чутливою до навантаження.
  3. Планувальники Linux і Windows навчилися бути SMT-обізнаними важким шляхом. Ранні планувальники ставили сіблінгів як незалежні ядра; це посилювало конкуренцію за ресурси. Сучасні планувальники намагаються пакувати й розпорошувати обробу розумно.
  4. Хмарні моделі вартості нормалізували «vCPU». «vCPU» може означати ціле ядро, сіблінг-потік або часову частку, залежно від провайдера й класу інстансу. Оператори отримали цю неоднозначність у спадок.
  5. Дослідження побічних каналів змінили дефолтну позицію ризику. SMT може збільшити площу витоку (спільні кеші й предиктори), через що деякі середовища йдуть шляхом «SMT вимкнено» або політик ядрового планування.
  6. Сучасні ядра стали ширшими й глибшими. Більша ширина виконання дає більше можливостей для SMT покращити використання — поки ви не вдарите в спільні вузькі місця, як пропускна здатність L1/L2.
  7. NUMA став мейнстримом. Переваги SMT можуть бути затьмарені штрафами за міжсокетний доступ до пам’яті; погане розміщення потоків може зробити SMT крайнім.
  8. Контейнеризація посилила ефекти планувальника. Cgroups, квоти й CPU-сети у взаємодії з SMT-сіблінгами можуть створювати «загадкове» пригнічення й латентність.

Що ділиться: незручний список

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

Спільний фронт-енд і виконавчі ресурси

  • Пропускна здатність вибірки/декоду інструкцій: два потоки борються за слоти декоду. Якщо обидва важкі — кожен отримує менше.
  • Виконавчі блоки: цілісні, дійсні, векторні, крипто-блоки — однаковий набір. Якщо обидва потоки використовують ті самі блоки, виникає пряма конкуренція.
  • Структури предиктора переходів: спільні предиктори можуть знизити точність при змішаних навантаженнях, підвищуючи штрафи за неправильні передбачення.
  • Ресурси завантаження/запису: буфери збереження та черги завантаження можуть насититися під інтенсивними по пам’яті сіблінгами.

Спільні кеші й пропускна здатність пам’яті

  • L1/L2 кеші: зазвичай діляться між SMT-сіблінгами (L1i/L1d і L2 на ядро). Тераш кешу — класичний результат «SMT зробив гірше».
  • Спільний кеш останнього рівня (LLC): ділиться між ядрами. SMT може збільшити тиск на LLC, дозволяючи більше одночасних промахів.
  • Пропускна здатність пам’яті: SMT може покращити використання ядра, але також збільшити навантаження на підсистему пам’яті. Якщо ви обмежені пропускною здатністю, SMT може погіршити хвостову латентність.

Що більш відокремлене, ніж думають

Кожен апаратний потік має власний архітектурний стан регістрів. Саме тому ОС може трактувати його як CPU. Але «стан» ≠ «пропускна здатність». Гарячий шлях — це все ще спільне ядро.

Висновок для оператора: SMT — це ставка, що ваше навантаження має затримки, які можна перекрити без боротьби за ті самі вузькі місця. Якщо ви вже обмежені шириною виконання, пропускною здатністю L1 або пам’яттю, SMT може просто додати конкуренцію.

Планувальники: де ховається «хитрість»

SMT стає цікавим у планувальнику. Завдання планувальника: вирішити, чи розмістити два готові потоки на логічних CPU-сіблінгах одного ядра, чи розпилити їх по окремих фізичних ядрах.

Пакувати vs розпорошувати: чому відповідь змінюється залежно від мети

Для пропускної здатності може бути корисно заповнити сіблінги одного ядра перед пробудженням іншого ядра. Це залишає інші ядра вільними (економія енергії) і може покращити локальність кешу, якщо потоки ділять дані.

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

Linux: обізнаність SMT, CFS і проблема «виглядає як ідл»

Completely Fair Scheduler (CFS) у Linux намагається балансувати готові задачі по CPU. SMT додає топологію: CPU групуються в ядра, ядра в сокети, сокети в NUMA-вузли. Планувальник використовує це, щоб вирішити, чи CPU «достатньо ідл», і що означає «ідл» з погляду SMT.

Ось підступ: логічний CPU-сіблінг може здаватися ідл навіть коли фізичне ядро зайняте. Для планувальника це все ще місце для запуску роботи. Для вашого бюджету латентності це пастка.

Windows і гіпервізори: схожа проблема, інші налаштування

Гіпервізори планують vCPU на pCPU. Якщо хост має SMT, то гіпервізору теж доводиться вирішувати, чи мають два vCPU ділити ядро. Деякі роблять це добре; деякі роблять так, як ви їх налаштували (і це може бути гірше).

На практиці: у KVM/VMware/Hyper-V середовищі треба трактувати SMT як топологічне обмеження, а не як випадкову деталь. «Ми маємо 32 vCPU» — це не відповідь; це початок дискусії.

Коли SMT допомагає, шкодить або вводить в оману

SMT зазвичай допомагає

  • Змішані навантаження: один потік інтенсивно обчислює, інший часто чекає пам’яті; вони доповнюють одне одного.
  • Високе I/O + робота в користувацькому просторі: потоки проводять час у блокуванні, прокидаються коротко, потім знову блокуються. SMT може зменшити порожні інтервали.
  • Компіляції і збірки: багато незалежних задач; пропуск зазвичай покращується, хоча не лінійно.
  • Деякі веб-навантаження: багато дрібних задач, часті затримки, помірна CPU-інтенсивність на потік.

SMT зазвичай шкодить

  • Системи з низькою латентністю: міняти детермінованість на пропуск — погана угода, якщо ви продаєте p99.
  • Обмежені пропускною здатністю пам’яті: два сіблінги можуть сильніше тиснути на L1/L2 і пам’ять, збільшуючи черги й латентність.
  • Векторні інтенсивні обчислення: якщо обидва потоки використовують широкі векторні блоки, виникає конкуренція без великого виграшу по перекриттю.
  • Кеш-чутливі бази даних на ядро: додатковий шум сіблінга може збільшити промахи кешу й контенцію замків, особливо при поганому пінуванні.

SMT «бриже» через оманливу утилізацію

Найпоширеніша діагностична помилка: ви дивитесь на завантаження CPU і робите висновок, що є «запас». З SMT утилізація розмазується по логічних CPU. Ви можете бути насичені по фізичних ядрах, але бачити «50%» на системі з 2-way SMT, якщо кожне фізичне ядро запускає по одному насиченому потоку, а сіблінги переважно ідл.

Якщо вам важлива продуктивність, думайте про фізичні ядра і cycles per instruction (CPI/IPС), а не про «CPU%».

Безпека й ізоляція: частина, за яку ніхто не хоче відповідати

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

Практичні поради:

  • У одновласних середовищах з довіреними робочими навантаженнями SMT зазвичай безпечний; рішення базується на продуктивності й передбачуваності.
  • У мультиорендних середовищах, особливо де орендарі не довірені, SMT може бути питанням політики. «Увімкнули для пропускної здатності» — це неадекватна безпекова аргументація.
  • Деякі організації пом’якшують ризик відключенням SMT, пінінгом орендарів на ядра або використанням контролів типу «core scheduling». Якщо ви не розумієте ізоляцію платформи — не прикидайтеся, що розумієте; вимірюйте й задавайте політику.

Цитата (парафраз ідеї) від Dr. Richard Cook: складні системи працюють у деградованому режимі; інциденти виникають через нормальну роботу й нормальні компроміси, а не через «рідкісні» помилки.

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

Сенс команд не в милуванні виводом. Сенс — ухвалити рішення: залишити SMT, вимкнути, змінити планування, зафіксувати робочі навантаження або перестати звинувачувати CPU і шукати справжнє вузьке місце.

Завдання 1: Підтвердити топологію CPU (ядер vs потоків)

cr0x@server:~$ lscpu | egrep 'Model name|Socket|Core|Thread|CPU\(s\)|NUMA node'
CPU(s):                               32
Model name:                           Intel(R) Xeon(R) CPU
Thread(s) per core:                   2
Core(s) per socket:                   8
Socket(s):                            2
NUMA node(s):                         2

Що це означає: 32 логічних CPU, 16 фізичних ядер, 2 сокети, SMT=2.

Рішення: Планування ємності й пінінг мають базуватись на ядрах і сокетах. «32 CPU» — не ваш реальний ліміт обчислень.

Завдання 2: Відобразити відношення сіблінгів (які логічні CPU ділять ядро)

cr0x@server:~$ for c in /sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list; do echo "$c: $(cat $c)"; done | head
/sys/devices/system/cpu/cpu0/topology/thread_siblings_list: 0,16
/sys/devices/system/cpu/cpu1/topology/thread_siblings_list: 1,17
/sys/devices/system/cpu/cpu2/topology/thread_siblings_list: 2,18
/sys/devices/system/cpu/cpu3/topology/thread_siblings_list: 3,19

Що це означає: cpu0 і cpu16 — сіблінги на одному фізичному ядрі, і так далі.

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

Завдання 3: Перевірити, чи SMT увімкнено під час роботи

cr0x@server:~$ cat /sys/devices/system/cpu/smt/active
1

Що це означає: 1 = SMT активний; 0 = SMT неактивний.

Рішення: Якщо ви розбираєтесь з непередбачуваністю, зафіксуйте цей стан в таймлайні інциденту. Це змінює інтерпретацію «CPU%».

Завдання 4: Перевірити статус ядра щодо уразливостей (часто впливає на політику SMT)

cr0x@server:~$ grep -H . /sys/devices/system/cpu/vulnerabilities/* | head
/sys/devices/system/cpu/vulnerabilities/l1tf:Mitigation: PTE Inversion; VMX: conditional cache flushes, SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/mds:Mitigation: Clear CPU buffers; SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/spectre_v2:Mitigation: Retpolines; STIBP: disabled; RSB filling

Що це означає: Платформа звітує «SMT vulnerable» для певних проблем.

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

Завдання 5: Знайти контенцію CPU і тиск черги виконання швидко

cr0x@server:~$ uptime
 15:42:11 up 23 days,  4:18,  2 users,  load average: 22.31, 20.87, 18.92

Що це означає: Load average ≈20 на машині з 16 ядрами може бути нормально або жахливо. SMT і «32 CPU» спонукають вас знехтувати цим. Не робіть цього.

Рішення: Порівнюйте load із фізичними ядрами. Якщо load постійно перевищує ядра і латентність висока — скоріше за все, у вас CPU-обмеження або блокування на невідривному I/O.

Завдання 6: Побачити, чи у вас нестача CPU або I/O-блокування (vmstat)

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
18  0      0 214512  92344 812304    0    0     2     8 9200 18000 62 18 18  2  0
20  0      0 214488  92344 812312    0    0     0     0 9401 19012 64 17 18  1  0
22  0      0 214460  92344 812320    0    0     0     4 9602 20011 66 16 17  1  0

Що це означає: Високий r (черга виконання) з низьким id вказує на CPU-тиск. Низький wa означає, що це не I/O wait.

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

Завдання 7: Побачити насичення по логічних CPU (mpstat) і знайти дисбаланс сіблінгів

cr0x@server:~$ mpstat -P ALL 1 1 | head -n 20
Linux 6.1.0 (server) 	01/09/2026 	_x86_64_	(32 CPU)

15:42:35     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %idle
15:42:36     all   58.20    0.00   16.10    1.20    0.00    1.10    0.00  23.40
15:42:36       0   95.00    0.00    4.00    0.00    0.00    0.00    0.00   1.00
15:42:36      16   12.00    0.00    3.00    0.00    0.00    0.00    0.00  85.00

Що це означає: CPU0 завантажений, тоді як його сіблінг CPU16 переважно ідл. Це не «нормально» — це вказує на один важкий потік, що насичує ядро.

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

Завдання 8: Ідентифікувати точні потоки та їх розміщення на CPU

cr0x@server:~$ ps -eLo pid,tid,psr,pcpu,comm --sort=-pcpu | head
 9123  9123  0  94.7 java
 9123  9155 16  12.1 java
 2201  2201  3   8.2 nginx

Що це означає: У процесі Java є потік на CPU0, що споживає ~95%. Інший потік цього процесу на CPU16 (сіблінг) робить менше.

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

Завдання 9: Перевірити CFS-тротлінг через квоти cgroup (контейнери + SMT = цікаво)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 9812331221
user_usec 7201123000
system_usec 2611208221
nr_periods 128833
nr_throttled 21992
throttled_usec 981223122

Що це означає: Навантаження часто тротлиться. Це може виглядати як «CPU ідл, але повільно», бо планувальник нав’язує квоти.

Рішення: Якщо тротлінг високий — виправте запити/ліміти CPU перш ніж звинувачувати SMT. SMT може підсилити сприйняття проблеми, бо облік по логічних CPU не корелює прямо з пропускною здатністю ядра.

Завдання 10: Виміряти шторми перемикань контексту (часто симптом сіблінг-конкуренції)

cr0x@server:~$ pidstat -w 1 3
Linux 6.1.0 (server) 	01/09/2026 	_x86_64_	(32 CPU)

15:43:10      UID       PID   cswch/s nvcswch/s  Command
15:43:11     1000      9123   2200.00   3100.00  java
15:43:11        0      2201    800.00   1200.00  nginx

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

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

Завдання 11: Шукати міграції CPU (ускорювач трешу кешу)

cr0x@server:~$ perf stat -e context-switches,cpu-migrations,task-clock -a -- sleep 5
 Performance counter stats for 'system wide':

        220,441      context-switches
         18,902      cpu-migrations
      160,002.11 msec task-clock

       5.001823308 seconds time elapsed

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

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

Завдання 12: Визначити, чи ви стоїте на пам’яті (промахи LLC, цикли, IPC)

cr0x@server:~$ perf stat -e cycles,instructions,cache-misses,LLC-load-misses -a -- sleep 5
 Performance counter stats for 'system wide':

  18,223,441,112      cycles
  10,002,112,009      instructions
     88,120,331      cache-misses
     21,002,114      LLC-load-misses

       5.001402114 seconds time elapsed

Що це означає: IPC ≈ 10.0B / 18.2B ≈ 0.55, що є низьким для багатьох серверних навантажень; багато LLC-промахів підказують, що ви стоїте на пам’яті.

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

Завдання 13: Перевірити афініті IRQ (погане розміщення IRQ може імітувати SMT-біль)

cr0x@server:~$ grep -E 'eth0|nvme|mlx' /proc/interrupts | head
  55:  1200033  0  0  0  IR-PCI-MSI 524288-edge      eth0-TxRx-0
  56:        0  0  0  0  IR-PCI-MSI 524289-edge      eth0-TxRx-1

Що це означає: Якщо переривання сконцентровані на CPU, який також виконує гарячі додатки (або його сіблінгу), ви можете отримати стрибки латентності.

Рішення: Розподіліть IRQ по фізичних ядрах (або ізолюйте їх) перш ніж робити SMT винуватцем.

Завдання 14: Перевірити використання CPU процесами проти ілюзії «CPU%»

cr0x@server:~$ top -b -n 1 | head -n 15
top - 15:44:12 up 23 days,  4:20,  2 users,  load average: 22.10, 21.03, 19.12
%Cpu(s): 58.1 us, 16.2 sy,  0.0 ni, 23.9 id,  1.0 wa,  0.0 hi,  0.8 si,  0.0 st
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   9123 app       20   0 12.1g   2.1g  302m R  620.0  6.7   188:22.11 java

Що це означає: Процес використовує ~6.2 логічних CPU часу. На системі 16-ядер/32-потоки це може бути «нормально» або «немає запасу», залежно від конкуренції сіблінгів і цілей p99.

Рішення: Порівнюйте попит процесу з фізичними ядрами і корелюйте з чергою виконання та perf-лічильниками, а не лише з «idle%» в top.

Завдання 15: Тимчасово відключити SMT (для контрольованого тестування)

cr0x@server:~$ echo off | sudo tee /sys/devices/system/cpu/smt/control
off

Що це означає: SMT тепер вимкнено (за умови підтримки, runtime). Деякі системи вимагають перезавантаження або налаштування прошивки.

Рішення: Виконайте A/B тест на репрезентативному навантаженні. Якщо p99 суттєво покращується з прийнятним падінням пропускної здатності — залишайте SMT вимкненим для цього шару.

Завдання 16: Підтвердити стан SMT після зміни

cr0x@server:~$ cat /sys/devices/system/cpu/smt/active
0

Що це означає: SMT неактивний.

Рішення: Перенастройте порогові значення: оповіщення по CPU% і пороги ємності потрібно змінити, бо «загальна кількість CPU» змінилася.

Жарт №2: Вимкнути SMT, щоб покращити латентність — це як зняти пасажирське сидіння, щоб покращити час кола. Працює, але колеги поставлять питання.

Швидкий план діагностики

Коли ви на виклику, часу на семінари немає. Потрібен короткий шлях до відповіді «CPU?», «пам’ять?», «I/O?» або «планувальник/cgroup-нісенітниця?» Ось практичний порядок, який уникатиме типових пасток.

Спершу: встановити топологію і чи «CPU%» вас обманює

  1. Топологія: lscpu і списки сіблінгів. Знати ядра vs потоки vs сокети.
  2. Черга виконання: vmstat 1 і uptime. Порівняйте load і r з фізичними ядрами.
  3. Насичення по CPU: mpstat -P ALL. Шукайте завантажені логічні CPU з ідл-сіблінгами.

По-друге: вирішити, чи це виконання CPU або пам’ять

  1. IPC + промахи кешу: perf stat -e cycles,instructions,LLC-load-misses.
  2. Міграції: perf stat -e cpu-migrations і pidstat -w.
  3. Гарячі потоки: ps -eLo ... --sort=-pcpu.

По-третє: виключити «фейковий CPU-тиск» від квот і переривань

  1. Тротлінг cgroup: cat /sys/fs/cgroup/cpu.stat (або еквівалент cgroup v2).
  2. Гарячі точки IRQ: /proc/interrupts і маски афініті.
  3. Перевірка I/O wait: vmstat і (якщо є) per-disk статистика. Високий wa змінює картину.

Якщо після цих кроків у вас: висока черга виконання, низький idle, низький IPC, багато LLC-промахів і стрибки латентності — SMT у списку підозрюваних. Якщо ж високий тротлінг або багато міграцій — SMT може бути невинним; проблема в плануванні й лімітах.

Поширені помилки: симптоми → корінь → виправлення

1) «CPU лише 50%, отже це не CPU»

Симптоми: поганий p99; top показує багато ідл; один або два потоки завантажені.

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

Виправлення: Дивіться per-logical CPU і пари сіблінгів; зафіксуйте гарячі потоки на ізольованих ядрах; розгортайте горизонтально або усувайте серіальні вузькі місця.

2) «Ми подвоїли vCPU, отже подвоїли ємність»

Симптоми: після переходу на інстанси з великою кількістю SMT пропуск зростає трохи, але хвостова латентність погіршується; росте ефект шумного сусіда.

Корінь: SMT дає часткове збільшення пропускної здатності, не лінійне; спільні ресурси підвищують взаємний вплив.

Виправлення: Перебазуватися з perf-лічильниками і p99; резервуйте ядра для рівнів з критичною латентністю; не ототожнюйте vCPU з ядрами у документах з розрахунку ємності.

3) «Вимкнення SMT виправить продуктивність»

Симптоми: SMT off покращує один метрик, але загальна пропускна здатність падає; витрати CPU зростають; деякі сервіси стають повільнішими.

Корінь: Навантаження орієнтоване на пропуск; воно виграло від перекриття затримок; SMT не був вузьким місцем.

Виправлення: Залиште SMT увімкненим; зосередьтесь на локальності пам’яті (NUMA), I/O, контенції замків або налаштуванні GC. Використовуйте пінінг лише там, де він дає передбачуваність.

4) «Прикріпити все — це ‘performance engineering’»

Симптоми: чудові бенчмарки, жахлива реальна робота; велика супровідна вартість; періодичні затримки, коли закріплений CPU отримує шторм IRQ.

Корінь: Надмірне пінування зменшує гнучкість планувальника і створює крихку прив’язку до топології та переривань.

Виправлення: Закріплюйте лише критичні потоки; дайте решті можливість плануватися. Явно керуйте афініті для IRQ і виділіть CPU для обслуговування.

5) «Контейнери ізольовані, отже SMT не важливий»

Симптоми: один контейнер стрибає, інший відчуває підвищену латентність навіть при окремих лімітах CPU.

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

Виправлення: Використовуйте cpusets для ізоляції, коли потрібна детермінованість; уникайте розміщення двох CPU-інтенсивних контейнерів на сіблінгах; моніторте тротлінг-лічильники.

6) «NUMA — це апаратна річ; її можна ігнорувати»

Симптоми: Дивний обрив продуктивності при масштабуванні потоків; багато LLC-промахів; міжсокетний трафік.

Корінь: Розподіл пам’яті й потоків по сокетах є неоптимальним; SMT стає відволіканням від віддаленого доступу до пам’яті.

Виправлення: Використовуйте NUMA-обізнане розміщення (numactl, cpusets); тримайте пам’ять і потоки локально; тестуйте SMT в межах сокета.

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

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

Компанія керує клієнтським API з жорсткими p99 цілями. Вони мігрували на новий флот із «вдвічі більше CPU» на папері. Закупівля радіє. Фінанси у захваті. On-call готується вивчати нове хобі: сторожування.

Перший великий день трафіку. Медіана латентності в межах норми. p99 і p99.9 періодично йдуть вбік. Autoscaling реагує пізно, бо середній CPU «лише» 55%. Канал інциденту заповнений скриншотами панелей, які виглядають заспокійливо — що є найбільш образливим доказом.

Хтось нарешті дивиться per-core: кілька критичних потоків «завалюють» фізичні ядра, а сіблінги майже ідл. Сервіс має кілька серіалізованих гарячих шляхів (логування та підписування запитів), які не масштабуються з додатковими потоками. SMT зробив використання виглядати комфортним, тоді як фактичний обмежувальний ресурс — однопотокова пропускна здатність — вже був на межі.

Виправлення не було миттєвим «вимкнути SMT». Спершу вони перестали обманювати себе: оповіщення змінили з «%CPU» на чергу виконання і per-core насичення; ємність переглянули у фізичних ядрах. Потім оптимізували серіальні шляхи коду і зафіксували найчутливіші потоки на виділених фізичних ядрах. SMT залишили вмикненим для загального флоту і вимкнули для найстрогішого шару.

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

Інша організація працює з конвеєром на зразок Kafka з важкою компресією. Інженер помітив високу завантаженість CPU і вирішив «максимізувати використання CPU», збільшуючи кількість робочих потоків, поки всі логічні CPU не будуть зайняті. На тесті виглядає блискуче. Усе на 95% завантаження. Презентація пишеться сама.

В продакшені хвостова латентність обробки повідомлень зростає. Лаг споживачів будується в пікові години. Система не падає; вона просто… повільніша в способі, який змушує сумніватися у виборі кар’єри.

Постмортем показує: система чутлива до пропускної здатності пам’яті й кешу. Компресія і декомпресія навантажують спільні виконавчі блоки і трешать кеші на ядро. Подвоєння готових потоків означає, що сіблінги борються за ту ж L1/L2 пропускну здатність, у той час як підсистема пам’яті обслуговує більше одночасних промахів. Пропуск не подвоївся; зросло очікування.

Повернення назад кількості потоків одразу покращило p99. Пізніше вони обережно повернули конкуренцію: по одному робочому на фізичне ядро для етапу компресії, більш гнучке планування там, де робота I/O-залежна. Також додали перевірки регресій на базі perf-лічильників, щоб «більше потоків» мала виправдання IPC і кеш-метриками, а не лише CPU%.

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

Платіжна платформа має багаторівневу архітектуру: stateless API, stateful база даних і черга. Нічого екзотичного. Команда не одержима мікрооптимізаціями. Вони одержимі повторюваними базовими лініями.

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

Одного дня оновлення ядра змінило пом’якшення і трохи змінило поведінку планувальника. Базовий тест виявив стабільну регресію p99 у шарі бази даних при увімкненому SMT. Зміна була невеликою, і її могли б списати в продакшені як «варіабельність трафіку», але лабораторне відтворення стабільне.

Вони ввели політику: вузли БД працюють зі SMT вимкненим; додаткові вузли працюють зі SMT увімкненим. Також документують чому: робоче навантаження БД чутливе до кеша і пріоритизує хвостову латентність. Результат не героїчний; він передбачуваний. А передбачуваність — це те, чим можна керувати о 3 ранку.

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

Чек-лист A: Вирішіть, чи SMT має бути увімкнений для рівня сервісу

  1. Класифікуйте мету: пріоритет пропускної здатності чи латентності?
  2. Виміряйте базу при SMT увімкненому: p50/p95/p99, пропускна здатність, рівень помилок.
  3. Виміряйте базу при SMT вимкненому: те саме навантаження, той самий трафік, та сама версія.
  4. Порівняйте за апаратними лічильниками: IPC, LLC-промахи, міграції, контекстні переключення.
  5. Вирішіть по рівнях: нормально, що різні політики SMT застосовуються для БД і stateless-вузлів.
  6. Оновіть пороги моніторингу: сигнали по CPU% змінюють значення, коли змінюється кількість логічних CPU.

Чек-лист B: Пінінг без самонавідувальних ран

  1. Ідентифікуйте пари сіблінгів через thread_siblings_list.
  2. Виберіть фізичні ядра для резервування під сервіс з критичною латентністю.
  3. Утримуйте IRQ-важкі пристрої поза цими ядрами (або ізолюйте IRQ на обслуговуючих CPU).
  4. Закріплюйте лише критичні потоки/процеси. Решта — нехай плавають.
  5. Перевіряйте міграції й контекстні переключення після пінінгу; якщо пінінг підвищує міграції — це запах проблеми.

Чек-лист C: Санітарія контейнерів і cgroup

  1. Перевірте лічильники тротлінгу; якщо вони високі — виправте ліміти перш ніж налаштовувати SMT.
  2. Використовуйте cpusets для ізоляції, коли потрібна детермінованість.
  3. Уникайте пакування двох CPU-інтенсивних контейнерів на сіблінгах, якщо важливий p99.
  4. Перевіряйте після змін під реалістичним навантаженням; синтетичні тести часто пропускають патології планувальника.

FAQ

1) Чи Hyper-Threading те саме, що «додаткові ядра»?

Ні. Це два апаратні потоки, що ділять одне ядро. Ви отримуєте додаткові контексти планувальника, але не подвоєні виконавчі ресурси.

2) Чи варто вимикати SMT для баз даних?

Часто для баз даних, чутливих до латентності й кешу, SMT-off може покращити передбачуваність p99. Але не робіть це по шаблону — A/B тестуйте з perf-лічильниками.

3) Чому використання CPU виглядає низьким, коли латентність висока?

Бо утилізація усереднюється по логічних CPU. Одне насичене фізичне ядро може бути приховане ідл-сіблінгами. Дивіться per-CPU статистику і чергу виконання.

4) Чи допомагає SMT одно-потоковій продуктивності?

Не безпосередньо. Однопотокова продуктивність — це про turbo, локальність кешу і мікроархітектуру. SMT може нашкодити, якщо сіблінг відбирає ресурси.

5) Чи може SMT спричиняти джиттер у реальному часі або низьколатентних системах?

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

6) У Kubernetes, чи «CPU» у запиті — це фізичне ядро?

Не обов’язково. Це юніт планування й квот. Без cpusets і ретельного розміщення навантаження можуть ділити ядра й сіблінги непередбачувано.

7) Якщо я вимкну SMT, чи завжди поліпшиться безпека?

Ви зменшите деяке перехресне розділення стану на одному ядрі, що зменшує деякі ризики побічних каналів. Але безпека — багаторівнева: пом’якшення, ізоляція орендарів і патчі теж важливі.

8) Яке найпростіше правило для операторів?

Для шарів, орієнтованих на пропускну здатність: SMT зазвичай увімкнений. Для шарів зі строгими вимогами до латентності: тестуйте SMT-off і розгляньте тримання сіблінгів у спокої для критичних потоків.

9) Чому продуктивність погіршилася після налаштування «більше потоків»?

Бо більше готових потоків підвищує контенцію, контекстні переключення, промахи кешу і тиск на пропускну здатність пам’яті. SMT полегшує тригер цих умов.

10) Який найкращий метрик, щоб вирішити, чи SMT допомагає?

Використовуйте кінцеві метрики сервісу (p99, пропускна здатність) плюс мікроархітектурні індикатори (IPC, LLC-промахи) під репрезентативним навантаженням. CPU% — недостатньо.

Висновок: практичні наступні кроки

Hyper-Threading — це не чарівні потоки. Це прийом підвищення використання, який іноді поводиться як безкоштовний обід, а іноді — як квартира з тонкими стінами. Ваше завдання — класифікувати навантаження, виміряти вузьке місце й вибрати політику, яку ви зможете пояснити під час інциденту.

  1. Перепишіть ментальну модель: ємність — це фізичні ядра і пропускна здатність пам’яті, а не кількість логічних CPU.
  2. Прийміть швидкий план діагностики: топологія → черга виконання → насичення по CPU → perf-лічильники → тротлінг cgroup.
  3. Розглядайте SMT як політику по рівнях: вмикайте там, де важлива пропускна здатність; розгляньте вимкнення (або ізоляцію сіблінгів) там, де важливий p99.
  4. Припиніть використовувати CPU% як заспокійливу ковдру: поєднуйте його з per-core переглядами, міграціями і IPC.

Якщо нічого іншого не зробите: проведіть контрольований A/B тест з SMT увімкненим та вимкненим для найчутливішого до латентності шару і збережіть докази. Думки дешеві; perf-лічильники — ні.

← Попередня
MariaDB vs Redis: патерни кешування, що пришвидшують сайти без втрати даних
Наступна →
Квоти та резервації ZFS: пара контролю простору, яку потрібно зрозуміти

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