Pentium FDIV: помилка у діленні з плаваючою крапкою, яка принизила Intel у світі

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

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

Баг Pentium FDIV — канонічна історія: дрібний, нудний дефект у блоці ділення з плаваючою крапкою, який перетворив флагманський процесор Intel на глобальний кейс про те, що «коректність — це функціональна вимога». Це не було крахом серверів. Було гірше: помилка, що могла ховатися у відкритому вигляді.

Що насправді сталося (і що означає FDIV)

FDIV — це інструкція x86 для ділення з плаваючою крапкою. У 1994 році дослідники та користувачі виявили, що деякі процесори Intel Pentium давали неправильні результати для невеликого набору операцій ділення з плаваючою крапкою. Не «трохи в останньому десятковому знакові», а помітно неправильні результати, які могли мати значення для чисельного програмного забезпечення.

Баг знаходився в апаратній реалізації алгоритму ділення в блоці плаваючої крапки процесора (FPU). За специфічних пар операндів CPU використовував неправильні проміжні значення, що давало неправильний частковий результат. Більшість діленнь були в порядку. Багато користувачів ніколи не стикалися з ним. Саме тому він став таким знаменитим: занадто рідкісний, щоби пройти повз, але достатньо реальний, щоби підривати довіру.

Інженери з надійності захоплюються режимами відмов. FDIV не був типовим інцидентом доступності; це був інцидент коректності. Такі інциденти важче виявляти. Ваші SLI не кричать. Дашборди залишаються зеленими. Ваші клієнти тихо втрачають гроші, а потім голосно втрачають довіру.

Одна цитата, яку варто надрукувати над кожним пультом виклику в продакшні: «Надія — не план.» — Генерал Гордон Р. Салліван. У світі коректності «ми ніколи не бачили цього» — це просто надія з гарнішим шрифтом.

Короткий жарт №1: Баг FDIV довів, що можна мати «високу доступність» і при цьому бути недоступним для правди.

Чому це було важливо: приховані помилки сильніші за гучні відмови

Якщо контролер зберігання падає, ви переключаєтеся на резерв. Якщо вузол кластера помирає, ви його замінюєте. Якщо CPU повертає неправильну відповідь з правильним рівнем впевненості? Ви можете ніколи не дізнатися, які рядки даних отруєні. Ви не можете «перезапустити» неправильну математику так само, як не можете fsck-ати підроблений журнал.

В операційному плані інцидент FDIV спричинив публічну дискусію про:

  • Апаратне забезпечення як частина довіреної обчислювальної бази. Ваша модель загроз — це не лише зловмисники; це ще й баги.
  • SLI коректності. Якщо ви не вимірюєте «правильно», ви вимірюєте лише «швидко».
  • Економіку відкликів. Замінювати чіпи дорого. Не замінювати їх — коштує репутації, іноді дорожче.
  • Комунікацію. Інженерні помилки можна пробачити; ухильна риторика зазвичай — ні.

Intel зрештою запропонувала заміну, але не раніше, ніж історія вирвалася у ЗМІ. Це важливо, бо це класична корпоративна помилка: ставитися до технічного дефекту як до PR-неприємності, а не до порушення цілісності.

Короткі факти та історичний контекст (те, що люди забувають)

  • Проблема спливла 1994 року, коли Pentium був преміум-брендом, а «плаваюча точка» ставала важливою не лише в академії.
  • Проблема була детермінованою для певних пар операндів: ті самі вхідні дані давали той самий неправильний результат. Це робило її відтворюваною, а не «космічним промінням».
  • Корінь проблеми — відсутні записи у таблиці пошуку, що використовувалася алгоритмом ділення (деталі нижче). Це був не округлювальний глухий кут, а дефект таблиці.
  • Більшість навантажень ніколи не помітили, бо не виконували достатньо чутливих операцій ділення з плаваючою крапкою, щоб потрапити в поганий випадок.
  • Широке використання електронних таблиць і фінансів збільшило радіус дії, бо «бізнес-математика» перейшла на десктопи, де Pentium був домінантним.
  • Стандарт IEEE 754 не був винуватцем; винен був спосіб реалізації. Стандарти не врятують від поганого кремнію.
  • Незалежна верифікація мала значення: проблема стала беззаперечною, бо люди могли відтворювати її на різних машинах.
  • Це вплинуло на культуру закупівель — більші покупці почали питати про виправлення, ревізії кроків і валідацію, а не лише про МГц.
  • Це передбачило сучасне мислення про «приховану корупцію даних», що тепер з’являється в зберіганні (контрольні суми), пам’яті (ECC) і розподілених системах (перевірка кінця в кінець).

Як працював баг FDIV: ділення з таблицею й відсутні записи

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

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

З перспективи SRE це кошмарний клас багів, бо:

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

Інженери іноді питають: чому тести не спіймали це? Бо простір вхідних значень плаваючої точки астрономічно великий, і «типові» тести зазвичай фокусуються на граничних значеннях (0, 1, степені двійки, денормали), а не на дивній середині, де кусають таблиці апроксимацій.

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

Відтворення й виявлення як SRE

Мабуть, у вас немає в стійці Pentium 1994 року (якщо маєте — будь ласка, не підключайте його ні до чого). Проте оперативний метод важливий: визначте відомий-поганий тест, проганяйте його на сегментах кластера, порівнюйте результати й ізолюйте за апаратним підписом.

Класичне відтворення використовує ретельно підібрані операнди, на яких результат FDIV у Pentium відрізняється від коректного ділення. Точні константи тут не головне; головне — збудувати каркас перехресної перевірки, який може виявити «CPU A не погоджується з CPU B» без необхідності знати правильну відповідь заздалегідь.

У продакшн-системах це часто виглядає так:

  • Виконуйте обчислення двічі різними реалізаціями (апаратна проти програмної, або дві бібліотеки).
  • Порівнюйте результати у межах прийнятної толерантності.
  • Ескалюйте, коли частота невідповідностей перевищує поріг.
  • Маркуйте виходи з походженням, щоби ідентифікувати хости з підозрілими результатами.

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

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

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

Завдання 1: Визначити модель CPU і stepping на хості

cr0x@server:~$ lscpu | egrep 'Model name|Vendor ID|CPU family|Model:|Stepping:'
Vendor ID:           GenuineIntel
Model name:          Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
CPU family:          6
Model:               79
Stepping:            1

Що означає вивід: Модель/stepping — це спосіб корелювати з відомими errata (включно з історичними, як FDIV, та сучасними, як пом’якшення спекулятивного виконання).

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

Завдання 2: З’ясувати версію мікрокоду (бо мікрокод інколи пом’якшує проблеми)

cr0x@server:~$ dmesg | grep -i microcode | tail -n 3
[    0.452112] microcode: microcode updated early to revision 0xb00003a, date = 2021-05-11
[    0.452948] microcode: CPU0 sig=0x406f1, pf=0x1, revision=0xb00003a
[    0.453015] microcode: Microcode Update Driver: v2.2.

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

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

Завдання 3: Інвентаризація підписів CPU по флоту (приклад через SSH fanout)

cr0x@server:~$ for h in app01 app02 app03; do echo "== $h =="; ssh $h "lscpu | egrep 'Model name|Stepping'"; done
== app01 ==
Model name:          Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
Stepping:            1
== app02 ==
Model name:          Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
Stepping:            1
== app03 ==
Model name:          Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
Stepping:            2

Що означає вивід: app03 відрізняється. Так рідкісні баги коректності стають «виникають лише по вівторках».

Рішення: Карантинуйте аутлаєри від чутливих навантажень до перевірки.

Завдання 4: Перевірити наявність і ввімкнення пам’яті ECC

cr0x@server:~$ sudo dmidecode -t memory | egrep -i 'Error Correction Type|Total Width|Data Width' | head -n 6
Error Correction Type: Multi-bit ECC
Total Width: 72 bits
Data Width: 64 bits
Error Correction Type: Multi-bit ECC
Total Width: 72 bits
Data Width: 64 bits

Що означає вивід: ECC не запобігає FDIV, але запобігає великому класу «неправильної математики» через битові фліпи в пам’яті.

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

Завдання 5: Шукати повідомлення про Machine Check у логах

cr0x@server:~$ journalctl -k | egrep -i 'mce|machine check|hardware error' | tail -n 5
Jan 21 08:11:02 server kernel: mce: [Hardware Error]: CPU 7: Machine Check: 0 Bank 5: bea0000000000108
Jan 21 08:11:02 server kernel: mce: [Hardware Error]: TSC 0 ADDR fef1c140 MISC d012000100000000
Jan 21 08:11:02 server kernel: mce: [Hardware Error]: PROCESSOR 0:406f1 TIME 1705824662 SOCKET 0 APIC 14 microcode b00003a

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

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

Завдання 6: Швидка перевірка плаваючої точки через програмну перехресну порівнянку (простий каркас)

cr0x@server:~$ python3 - <<'PY'
import random, math, decimal
decimal.getcontext().prec = 80
def check(n=20000):
    bad = 0
    for _ in range(n):
        a = random.uniform(1e-100, 1e100)
        b = random.uniform(1e-100, 1e100)
        # hardware float
        hf = a / b
        # high precision decimal
        da = decimal.Decimal(str(a))
        db = decimal.Decimal(str(b))
        df = da / db
        # compare within relative tolerance
        if hf != 0.0:
            rel = abs((decimal.Decimal(hf) - df) / df)
            if rel > decimal.Decimal("1e-12"):
                bad += 1
    return bad
print("mismatches:", check())
PY
mismatches: 0

Що означає вивід: «0 mismatches» не доводить ідеальності; це знижує підозру. Якщо кількість невідповідностей зростає — у вас інцидент коректності.

Рішення: Якщо mismatches > 0, розширюйте розслідування: той самий код на різних хостах, перевірте підписи CPU, параметри компілятора та математичні бібліотеки.

Завдання 7: Визначити флаги компілятора, що змінюють чисельну поведінку

cr0x@server:~$ gcc -Q -O2 --help=optimizers | egrep 'fast-math|unsafe-math|finite-math-only|fp-contract' | head -n 6
  -ffast-math                 		[disabled]
  -funsafe-math-optimizations 		[disabled]
  -ffinite-math-only          		[disabled]
  -ffp-contract               		[off]

Що означає вивід: Підхід «fast-math» може легально порушувати очікування IEEE. Це «оптимізація» з заставою на коректність.

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

Завдання 8: Підтвердити, яку libm / libc ви реально запускаєте

cr0x@server:~$ ldd --version | head -n 2
ldd (Ubuntu GLIBC 2.35-0ubuntu3.4) 2.35
Copyright (C) 2022 Free Software Foundation, Inc.

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

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

Завдання 9: Підтвердити флаги CPU, що впливають на поведінку з плаваючою точкою

cr0x@server:~$ grep -m1 -oE 'sse2|sse4_2|avx2|fma' /proc/cpuinfo | sort -u
avx2
fma
sse2
sse4_2

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

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

Завдання 10: Прив’язати робоче навантаження до певної моделі CPU/сегмента (операційна ізоляція)

cr0x@server:~$ taskset -c 0-3 ./risk_calc --portfolio P42
OK: computed VaR=1.873e6 in 2.14s (threads=4)

Що означає вивід: Це фіксує виконання на підмножині ядер. Не вирішує FDIV, але допомагає ізолювати й відтворити проблеми на відомих ядрах/CPU.

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

Завдання 11: Виявити гетерогенність у контейнерах/Kubernetes вузлах (модель CPU як обмеження планування)

cr0x@server:~$ kubectl get nodes -o wide
NAME     STATUS   ROLES    AGE   VERSION   INTERNAL-IP   OS-IMAGE             KERNEL-VERSION
node-a   Ready    worker   92d   v1.28.3   10.0.1.11     Ubuntu 22.04.3 LTS   5.15.0-91-generic
node-b   Ready    worker   92d   v1.28.3   10.0.1.12     Ubuntu 22.04.3 LTS   5.15.0-91-generic
node-c   Ready    worker   18d   v1.28.3   10.0.1.13     Ubuntu 22.04.3 LTS   5.15.0-91-generic

Що означає вивід: Різниця в віці вузлів часто корелює з апаратними відмінностями. Там народжується «той самий застосунок — різні відповіді».

Рішення: Додавайте мітки вузлів за моделлю/stepping CPU і плануйте критичні навантаження цілеспрямовано.

Завдання 12: Маркувати вузли за моделлю CPU, щоб забезпечити розміщення

cr0x@server:~$ kubectl label node node-c cpu.intel.com/model=79
node/node-c labeled

Що означає вивід: Стабільний ідентифікатор для правил планування.

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

Завдання 13: Підтвердити, що математична бібліотека використовує узгоджений режим округлення (санітарна перевірка рантайму)

cr0x@server:~$ python3 - <<'PY'
import decimal
print("decimal rounding:", decimal.getcontext().rounding)
PY
decimal rounding: ROUND_HALF_EVEN

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

Рішення: Стандартизувати політику округлення в коді й документувати її як контракт API.

Завдання 14: Виявити, чи ядро повідомляє про вразливості процесора / стан пом’якшень

cr0x@server:~$ grep -H . /sys/devices/system/cpu/vulnerabilities/* | head -n 5
/sys/devices/system/cpu/vulnerabilities/l1tf:Mitigation: PTE Inversion
/sys/devices/system/cpu/vulnerabilities/meltdown:Mitigation: PTI
/sys/devices/system/cpu/vulnerabilities/spectre_v1:Mitigation: usercopy/swapgs barriers and __user pointer sanitization
/sys/devices/system/cpu/vulnerabilities/spectre_v2:Mitigation: Retpolines; IBPB: conditional; IBRS_FW
/sys/devices/system/cpu/vulnerabilities/tsx_async_abort:Mitigation: Clear CPU buffers; SMT vulnerable

Що означає вивід: Показує, що ОС в курсі проблем на рівні CPU. Це інша тема, ніж FDIV, але той самий мета-урок: кремній має errata; ви мусите ними керувати.

Рішення: Відстежуйте ці стани як інвентар. Якщо ви не можете пояснити стан пом’якшень, ви не можете пояснити свій ризик.

План швидкої діагностики

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

Перше: підтвердьте, що це реально (і обмежте радіус ураження)

  1. Отримайте мінімальний репроюсер. Одні й ті самі входи, та сама функція, та саме відхилення. Якщо ви не можете відтворити — ви не можете виправити.
  2. Перевірте, чи детермінована невідповідність. Детермінованість вказує на реалізацію/платформу. Недетермінованість вказує на гонку, неініціалізовану пам’ять або невизначену поведінку.
  3. Порівняйте результати на двох машинах. Якщо один хост відрізняється, у вас є дельта середовища, за якою треба гнатися.

Друге: ретельно зніміть підпис середовища

  1. Модель/stepping CPU та мікрокод. Якщо відрізняються — вважайте це релевантним, поки не доведете протилежне.
  2. Компілятор і прапори. Шукайте fast-math, FMA, відмінності в упакуванні та попередження санітизаторів.
  3. Версії математичних бібліотек. libm і бэкэнди BLAS — часті підозрювані.

Третє: ізолюйте за методом, а не за припущеннями

  1. Запустіть еталон високої точності. Використайте decimal/bigfloat або інструменти на базі MPFR як орієнтир для перевірок.
  2. Запустіть програмний fallback (якщо є) і порівняйте. Розбіжність звинуватить апарат або низькорівневу генерацію коду.
  3. Заблокуйте режим округлення й denormal-и. Невідповідний FP-оточення породжує «примарні» відмінності.

Короткий жарт №2: Якщо ваш постмортем інциденту містить «плаваюча точка — дивна», ви не знайшли причину — ви знайшли виправдання.

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

Міні-історія 1: Інцидент, спричинений хибним припущенням

Середня фінтех-компанія виконувала щоденні розрахунки ризику на батч-кластері. Завдання було «соромно паралельним»: розбили портфелі, порахували метрики, злили результати. Роки працювало нормально. Потім вони оновили частину вузлів — новіші CPU, той самий образ ОС, той самий контейнер. Вони припустили: «обчислення — це обчислення».

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

Прорив стався, коли хтось запустив той самий слайс портфеля на двох різних вузлах і отримав різні значення на 10–12 знаках після коми, що достатньо, щоби змінити пороговий тест далі в пайплайні. Корінь проблеми не був сучасним FDIV-типу дефектом: це була зміна в шляхах виконання плаваючої точки: нові CPU вмикали FMA, старі — ні, і бекенд BLAS поводився по-різному.

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

Урок FDIV підходить: апаратна складова — частина вашого API-інтерфейсу. Ігноруєте її — будете дебажити власну довіру.

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

Команда платформи машинного навчання мала проблему з витратами: інференс був дорогим, продакт хотів зменшити p95 латентності. Інженер ввімкнув прапор компілятора: агресивні оптимізації для плаваючої точки і перестроїв BLAS під найновіший набір інструкцій флоту.

Латентність зменшилася. Усі раділи. Потім спрацював моніторинг моделі: дрейф розподілу прогнозів для вузького підмножини вхідних даних. Ні краху. Ні сплеску. Тихий, постійний зсув. Модель не змінювалася; змінилася чисельна поведінка.

«Оптимізація» змінила асоціативність та округлення достатньо, щоб деякі граничні випадки переходили через поріг класифікації. Більшість прогнозів були ідентичні. Ті, що мали значення — ні.

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

FDIV не стався через увімкнений прапор. Але корпоративний режим помилок той самий: ставитися до коректності як до опціональної властивості — небезпечно. Коректність — не предмет торгу.

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

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

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

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

Виправлення було процедурним і нудним: стандартизувати FP-оточення, фіксувати критичні прогони на валідаційному класі вузлів і тримати gate з відбитком. Також вони задокументували допустимі межі варіації чисел для кожного типу моделі. Урок: нудні пропуски — те, що повертає вам сон.

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

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

1) Симптом: «Тільки один клієнт це бачить»

Корінь: Навантаження/вхідні дані-специфічні — тригериться рідкісний шаблон операндів або граничний випадок.

Виправлення: Захопіть точні вхідні дані клієнта і відтворіть їх у двох середовищах. Додайте канарну валідацію для цього класу входів.

2) Симптом: «Стейджінг не відтворює продакшн»

Корінь: Відмінності у stepping CPU, мікрокоді або наборі інструкцій між середовищами. Контейнери цього не вирішують.

Виправлення: Побудуйте класи середовищ (мітки) і вимагайте, щоб стейджінг відповідав продакшн-класу для тестів коректності.

3) Симптом: «Це випадково; повторний запуск це фіксує»

Корінь: Гонки даних, неініціалізована пам’ять, невизначена поведінка або недетермінований порядок згортувань у паралельних обчисленнях.

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

4) Симптом: «Розбіжність маленька, тож це нормально»

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

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

5) Симптом: «Почалося після оптимізації продуктивності»

Корінь: fast-math, увімкнення FMA, відмінності в векторизації або зміна бекенда BLAS.

Виправлення: Тримайте дві профілі збірки (строга і швидка). Пропуск швидкої профілі постачайте регресійними тестами чисельності і задокументованим бюджетом помилки.

6) Симптом: «Відбувається лише на одному стояку/регіоні»

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

Виправлення: Зберіть інвентар CPU/мікрокоду, перевірте журнали MCE і карантинуйте підозріле обладнання. Не сперечайтесь з фізикою.

7) Симптом: «Контрольні суми проходять, але аналітика відхиляється»

Корінь: Пайплайн даних цілий; обчислення помилкові. Цілісність сховища не гарантує цілісності обчислень.

Виправлення: Додайте перевірку кінця в кінець: перекомпілюйте вибірки на відомому-правильному еталоні і порівняйте.

Перевірочні списки / покроковий план

Коли ви підозрюєте проблему на рівні CPU

  1. Заморозьте входи: захопіть точні payload-и, seed-и і конфіг. Жодного «майже підходить».
  2. Відтворіть на тому самому хості двічі. Якщо результат змінюється між запусками, ви, ймовірно, женетеся за недетермінізмом, а не за кремнієм.
  3. Відтворіть на іншому хості з іншим апаратним підписом. Якщо один клас хостів не погоджується — у вас є рука для сегментації.
  4. Зніміть відбиток платформи: модель/stepping CPU, мікрокод, ядро, libc/libm, digest контейнера, версія компілятора.
  5. Перехресна перевірка з еталоном: вища точність або альтернативна реалізація для репрезентативної вибірки.
  6. Ізоляція: прив’язуйте чутливі навантаження до валідаційного класу обладнання; спустошуйте підозрілі вузли.
  7. Комунікація: інциденти коректності потребують чітких повідомлень стейкхолдерам — що постраждало, що ні і як ви це знаєте.
  8. Виправлення: замініть/виведіть з експлуатації обладнання, якщо воно причетне, або стандартизируйте рантайм/прапори, якщо проблема в ПЗ.
  9. Попередження повторення: додайте ворота з чисельними відбитками і розкладення по класах середовища.

Що стандартизувати у продакшн-флоті (щоб спати спокійно)

  • Інвентар CPU, що відстежується як залежність: модель, stepping, мікрокод.
  • Золоті класи вузлів для навантажень, критичних до коректності.
  • Профілі збірки: строгий IEEE-профіль для робіт з цілісністю; швидкий профіль — лише з явним дозволом.
  • Каркаси валідації, що можуть запускатися за запитом: перехресне порівняння, вибіркова перевірка з еталоном.
  • Метадані походження: маркуйте результати класом хоста, ID збірки і версіями бібліотек для трасування.

Питання й відповіді

1) Чи був баг Pentium FDIV софтверним чи апаратним?

Апаратним. Це була помилка в реалізації логіки ділення в FPU Pentium, пов’язана з неправильними записами у таблиці пошуку, що використовувалася під час ділення.

2) Наскільки часто з’являлися неправильні результати?

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

3) Чи можна було виправити FDIV програмним патчем?

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

4) Чому звичайне тестування не впіймало це?

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

5) Який сучасний еквівалент ризику?

Будь-яке джерело прихованої корупції даних: маргінальне обладнання (пам’ять, CPU), невизначена поведінка в коді, агресивні прапори компілятора для математики, невідповідні FP-оточення у гетерогенних флотах і баги в чисельних бібліотеках.

6) Як захистити розподілену систему від прихованих математичних помилок?

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

7) Чи бувають у GPU проблеми «класу FDIV»?

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

8) Чи «fast-math» завжди неправильний?

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

9) Як має виглядати реагування на інцидент коректності?

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

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

Баг Pentium FDIV — це не лише ретро-розмови про комп’ютери. Це довічний операційний урок: помилки коректності не оголошують себе, і «рідко» — не те саме, що «безпечно». Якщо ви запускаєте продакшн-системи, де числа стають рішеннями, вам потрібен план, який ставить цілісність обчислень на перше місце.

  1. Проінвентаризуйте свій флот обчислень за моделлю/stepping CPU і мікрокодом; відстежуйте це як залежність.
  2. Створіть валідаційні класи вузлів і цілеспрямовано плануйте на них критичні навантаження.
  3. Впровадьте строгий профіль збірки для математики і забороніть небезпечні прапори за замовчуванням.
  4. Додайте ворота чисельних відбитків для ключових задач: фіксовані вхідні дані, допустима погрішність, метадані походження.
  5. Побудуйте runbook для on-call по коректності, що починається з перехресного порівняння по хостах і закінчується ізоляцією, а не спекуляціями.

Якщо ви оперуєте лише для швидкості, рано чи пізно ви відправите швидке нісенітництво. Баг FDIV зробив цей урок публічним. Вам не потрібен світовий сором, щоб вчитися на ньому приватно.

← Попередня
Флаги функцій ZFS: правила сумісності між хостами та версіями
Наступна →
Інтегровані контролери пам’яті: зміна, яку ви відчуваєте й досі

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