Pentium FDIV : le bug mathématique qui a mis Intel dans l’embarras mondial

Cet article vous a aidé ?

Rien en exploitation n’est plus effrayant qu’un système rapide, stable et faux. On peut être alerté par la latence. On peut être alerté par des plantages. Mais si votre couche de calcul se trompe silencieusement et renvoie quand même un nombre parfaitement plausible ? C’est ainsi qu’on envoie de mauvaises transactions, de mauvais risques, de la mauvaise science et de mauvaises factures—à grande échelle.

Le bug Pentium FDIV est l’exemple canonique : un petit défaut en apparence dans une unité de division en virgule flottante qui a transformé le CPU phare d’Intel en étude de cas mondiale sur le thème « la correctitude est une fonctionnalité ». Ce n’était pas un effondrement de serveurs. C’était pire : une erreur qui pouvait se cacher en plein jour.

Ce qui s’est réellement passé (et ce que signifie FDIV)

FDIV est l’instruction x86 de division en virgule flottante. En 1994, des chercheurs et des utilisateurs ont découvert que certains processeurs Intel Pentium produisaient des résultats incorrects pour un petit ensemble d’opérations de division en virgule flottante. Pas « un peu d’erreur dans le dernier chiffre », mais des erreurs mesurables pouvant affecter des logiciels numériques.

Le bug se trouvait dans l’implémentation matérielle de l’algorithme de division dans l’unité en virgule flottante (FPU) du Pentium. Pour des motifs d’opérandes spécifiques, le CPU utilisait des valeurs intermédiaires incorrectes, produisant un quotient erroné. La plupart des divisions étaient correctes. Beaucoup de gens ne l’ont jamais rencontré. C’est précisément pourquoi il est devenu fameux : assez rare pour échapper aux contrôles, mais réel au point d’ébranler la confiance.

Les ingénieurs fiabilité s’obsèdent sur les modes de défaillance. FDIV n’était pas un incident d’accessibilité typique ; c’était un incident de correctitude. Ceux-là sont plus difficiles. Vos SLI ne crient pas. Vos tableaux de bord restent verts. Vos clients perdent silencieusement de l’argent puis perdent bruyamment confiance.

Une citation à imprimer au-dessus de chaque poste d’astreinte production : « L’espoir n’est pas une stratégie. » — Général Gordon R. Sullivan. Dans le domaine de la correctitude, « nous ne l’avons jamais vu » n’est que de l’espoir avec une police plus jolie.

Petite blague #1 : Le bug FDIV a prouvé qu’on peut avoir de la « haute disponibilité » et être malgré tout indisponible pour la vérité.

Pourquoi ça importait : les erreurs silencieuses surpassent les pannes bruyantes

Si un contrôleur de stockage panique, vous basculez. Si un nœud de cluster meurt, vous le remplacez. Si un CPU renvoie la mauvaise réponse avec la bonne confiance ? Vous ne saurez peut‑être jamais quelles lignes sont empoisonnées. Vous ne pouvez pas « redémarrer » un mauvais calcul plus qu’on ne peut réparer un grand livre falsifié par un fsck.

En termes opérationnels, l’incident FDIV a forcé une conversation publique autour de :

  • Le matériel comme partie de votre base de confiance calculatoire. Votre modèle de menace n’inclut pas que des attaquants ; il inclut des bugs.
  • Des SLI de correctitude. Si vous ne mesurez pas le « juste », vous ne mesurerez que le « rapide ».
  • L’économie des rappels. Remplacer des puces coûte de l’argent. Ne pas les remplacer coûte la crédibilité—parfois davantage.
  • La communication. Les erreurs d’ingénierie peuvent être pardonnées ; les messages évasifs, rarement.

Intel a finalement proposé des remplacements, mais pas avant que l’affaire n’éclate dans les médias grand public. Cette partie compte parce que c’est un mode d’échec corporatif classique : traiter un défaut technique comme une contrariété de RP, au lieu d’une atteinte à l’intégrité.

Faits rapides et contexte historique (ce que les gens oublient)

  • Il est apparu en 1994, à une époque où Pentium était une marque premium et où la « virgule flottante » devenait pertinente hors du seul milieu académique.
  • Le problème était déterministe pour certaines paires d’opérandes : mêmes entrées, même sortie fausse. Cela le rendait reproductible, pas un fantôme cosmique.
  • La cause racine était des entrées manquantes dans une table de consultation utilisée par l’algorithme de division (détails ci‑dessous). Ce n’était pas un coin d’arrondi ; c’était un défaut de table.
  • La plupart des charges de travail ne le remarquaient pas parce qu’elles n’effectuaient pas assez de divisions sensibles pour rencontrer les cas défectueux.
  • L’utilisation en tableur et en finance a amplifié la portée parce que les « mathématiques métier » avaient migré vers des postes de travail où Pentium régnait.
  • La norme IEEE 754 n’était pas la coupable ; c’était l’implémentation. Les standards ne vous protègent pas d’un mauvais silicium.
  • La vérification indépendante a compté : le problème est devenu indéniable parce que des gens ont pu le reproduire sur plusieurs machines.
  • Il a influencé la culture d’achat—plus d’acheteurs ont commencé à demander les errata, les révisions stepping et la validation, pas seulement les MHz.
  • Il annonçait la réflexion moderne sur la « corruption silencieuse des données » que l’on retrouve aujourd’hui dans le stockage (checksums), la mémoire (ECC) et les systèmes distribués (validation bout en bout).

Comment le bug FDIV fonctionnait : division par table et entrées manquantes

La division matérielle est coûteuse en portes logiques et en latence. Les CPU utilisent des algorithmes astucieux pour approximer les réciproques puis les affiner. Dans la FPU du Pentium, l’opération de division utilisait une table de consultation pour amorcer l’approximation. Cette table associe certains bits du diviseur à des constantes qui pilotent un processus d’affinage itératif (pensez « commencer proche, puis converger »).

Voici le mode de défaillance clé : certaines entrées de la table étaient erronées (effectivement manquantes/azimutées), si bien que l’approximation initiale pouvait être suffisamment éloignée pour que l’affinage converge vers un résultat incorrect. Pas complètement faux, mais en dehors de l’erreur acceptable en virgule flottante.

Du point de vue d’un SRE, c’est une classe de bug cauchemardesque parce que :

  • C’est dépendant des entrées et donc apparaît « aléatoire » en production, même si c’est déterministe.
  • Cela peut être dépendant des données, c’est-à-dire que seuls certains jeux de données le déclenchent (une distribution particulière de diviseurs).
  • C’est non‑bloquant. Il n’y a pas de signal à moins de valider les sorties.
  • C’est spécifique à la plate-forme. Votre environnement de staging peut ne pas correspondre au stepping de production.

Les ingénieurs demandent parfois : pourquoi les tests ne l’ont-ils pas attrapé ? Parce que l’espace des entrées en virgule flottante est astronomiquement grand, et que les tests « typiques » ont tendance à se concentrer sur valeurs frontières (0, 1, puissances de deux, sous-normales) plutôt que sur le milieu étrange où les tables d’approximation vous mordent.

La vérification matérielle s’est améliorée avec le temps, mais la leçon profonde reste : la correctitude exige une vérification indépendante, pas seulement la foi envers un fournisseur, une norme ou une exécution CI propre.

Reproduire et détecter comme un SRE

Vous n’avez probablement pas un Pentium de 1994 dans une armoire (si oui, ne le connectez à rien). Néanmoins, la méthode opérationnelle importe : définissez un test connu‑défectueux, exécutez‑le sur des segments du parc, comparez les résultats et isolez par signature matérielle.

Une reproduction classique utilise des opérandes choisis avec soin où le résultat FDIV du Pentium diverge de la division correcte. Les constantes exactes ne sont pas l’essentiel ici ; l’essentiel est de construire un harnais de vérification croisée capable de détecter « le CPU A n’est pas d’accord avec le CPU B » sans exiger de connaître la vérité absolue à l’avance.

En production, cela ressemble souvent à :

  • Exécuter les calculs deux fois avec des implémentations différentes (matériel vs logiciel, ou deux bibliothèques).
  • Comparer les résultats dans une enveloppe de tolérance acceptable.
  • Escalader quand le taux de divergences dépasse un seuil.
  • Marquer les sorties avec leur provenance pour identifier quels hôtes ont produit des résultats suspects.

C’est la même philosophie que pour le stockage quand vous faites des checksums et du scrubbing : faire confiance, mais vérifier—et vérifier bout en bout.

Tâches pratiques : commandes, sorties et décisions (12+)

Voici des tâches opérationnelles réelles que vous pouvez exécuter sur des flottes Linux pour réduire le risque de « mauvais calculs silencieux », identifier l’hétérogénéité et construire des garde‑fous. Chaque tâche inclut : commande, sortie d’exemple, ce que cela signifie et quelle décision prendre.

Task 1: Identify CPU model and stepping across a host

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

Signification de la sortie : Le modèle/stepping permet de corréler avec les errata connus (y compris des cas historiques comme FDIV, et des cas modernes comme les atténuations de l’exécution spéculative).

Décision : Si vous avez des steppings mixtes, planifiez une validation de correctitude par segment et affectez les charges à haute intégrité aux steppings reconnus bons.

Task 2: Capture microcode version (because microcode sometimes mitigates issues)

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.

Signification de la sortie : Confirme si vous exécutez un microcode à jour. FDIV était un défaut matériel de table et non « corrigeable par microcode » en pratique, mais beaucoup de problèmes CPU modernes le sont.

Décision : Si le microcode est obsolète, traitez‑le comme un retard de patch de sécurité. Déployez‑le selon votre cadence de mise à jour noyau.

Task 3: Inventory fleet CPU signatures at scale (example using 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

Signification de la sortie : app03 diffère. C’est ainsi que des bugs de correctitude rares deviennent « ça n’arrive que le mardi ».

Décision : Quarantainez les éléments atypiques pour les charges sensibles jusqu’à validation.

Task 4: Check whether ECC memory is present and enabled

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

Signification de la sortie : L’ECC n’empêche pas FDIV, mais évite une grande classe de « mauvais calculs » dus aux basculements de bits en mémoire.

Décision : Si un segment de flotte n’a pas d’ECC, n’y exécutez pas de charges numériques à haute intégrité. Point final.

Task 5: Look for machine check events (hardware errors) in logs

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

Signification de la sortie : Indique une instabilité matérielle sous-jacente. Pas spécifique à FDIV, mais ça montre qu’on ne peut pas faire confiance à la plateforme aveuglément.

Décision : Si des MCE apparaissent, planifiez un remplacement matériel et déplacez immédiatement les jobs critiques pour la correctitude.

Task 6: Run a quick floating-point self-check with software cross-compare (simple harness)

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

Signification de la sortie : « 0 mismatches » ne prouve pas la perfection ; cela réduit le soupçon. Si les divergences montent, vous avez un incident de correctitude.

Décision : Si mismatches > 0, élargissez l’investigation : même code sur d’autres hôtes, vérifiez les signatures CPU, validez les flags du compilateur et les bibliothèques math.

Task 7: Identify compiler flags that may change numerical behavior

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]

Signification de la sortie : Les flags de type fast-math peuvent légalement briser les attentes IEEE. C’est de l’« optimisation » avec une hypothèque sur la correctitude.

Décision : Pour la finance/la science/la cryptographie/la traçabilité, bannissez ces flags dans les builds de production à moins d’avoir prouvé leur innocuité.

Task 8: Verify which libm / libc you are actually running

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

Signification de la sortie : Différentes implémentations de libm peuvent différer sur les cas limites. Si vous déboguez une dérive numérique, vous devez connaître le runtime exact.

Décision : Standardisez les runtimes sur les flottes de calcul, sinon vous poursuivrez des « bugs » qui ne sont que des différences de bibliothèques.

Task 9: Confirm CPU flags relevant to floating-point behavior

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

Signification de la sortie : Les jeux d’instructions influent sur la numérics (FMA change le comportement d’arrondi) et sur les chemins d’exécution dans les bibliothèques.

Décision : Si les résultats diffèrent entre hôtes, vérifiez si des flags différents provoquent des chemins d’exécution distincts.

Task 10: Pin a workload to a specific CPU model/segment (operational containment)

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

Signification de la sortie : Cela fixe l’exécution à un sous‑ensemble de cœurs. Ce n’est pas une correction pour FDIV, mais une technique pour isoler et reproduire des problèmes sur des cœurs/CPU connus.

Décision : Si seuls certains cœurs/CPU se comportent mal (rare, mais possible sur du matériel marginal), la contention vous donne du temps.

Task 11: Detect heterogeneity in container/Kubernetes nodes (CPU model as a scheduling constraint)

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

Signification de la sortie : Les différences d’âge corrèlent souvent avec des différences matérielles. C’est là que naît le « même appli, réponses différentes ».

Décision : Ajoutez des labels de nœud basés sur le modèle/stepping CPU et ordonnez les workloads critiques pour la correctitude explicitement.

Task 12: Label nodes by CPU model to enforce placement

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

Signification de la sortie : Un identifiant stable pour les règles d’ordonnancement.

Décision : Utilisez l’affinité de nœud pour assurer la cohérence, surtout lors de la validation des régressions numériques.

Task 13: Confirm math library uses consistent rounding mode (runtime sanity)

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

Signification de la sortie : HALF_EVEN est courant en contexte financier ; des règles d’arrondi incohérentes entre services peuvent ressembler à des « bugs matériels ».

Décision : Standardisez les politiques d’arrondi dans le code et documentez‑les comme un contrat d’API.

Task 14: Detect if the kernel is reporting CPU vulnerabilities/mitigation state

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

Signification de la sortie : Montre que l’OS connaît des problèmes au niveau CPU. Sujet différent de FDIV, même leçon méta : le silicium a des errata ; vous devez les gérer.

Décision : Suivez ces états comme un inventaire. Si vous ne pouvez pas expliquer votre état d’atténuation, vous ne pouvez pas expliquer votre risque.

Mode opératoire pour diagnostic rapide

Voici la séquence « cessez de théoriser et obtenez du signal » quand quelqu’un dit « les chiffres ne collent pas » et que vous suspectez un problème de hardware, de compilateur ou de bibliothèque. L’objectif est d’identifier rapidement le goulot d’enquête : les entrées, la plateforme ou l’implémentation.

First: confirm it’s real (and bound the blast radius)

  1. Obtenez un reproduiseur minimal. Mêmes entrées, même fonction, même divergence de sortie. Si vous ne pouvez pas reproduire, vous ne pouvez pas corriger.
  2. Vérifiez si la divergence est déterministe. Déterministe → implémentation/plateforme. Non‑déterministe → concurrence, mémoire non initialisée ou comportement indéfini.
  3. Comparez les résultats entre deux machines. Si un hôte diffère, vous avez un delta d’environnement matériel/logiciel à traquer.

Second: fingerprint the environment like you mean it

  1. Modèle/stepping CPU et microcode. S’ils diffèrent, considérez‑les pertinents jusqu’à preuve du contraire.
  2. Compilateur et flags. Cherchez fast-math, différences FMA, et avertissements des sanitizers pour comportement indéfini.
  3. Versions des bibliothèques mathématiques. libm et backends BLAS sont des coupables fréquents.

Third: isolate by method, not by vibes

  1. Exécutez une référence haute précision. Utilisez decimal/bigfloat ou des outils basés sur MPFR comme oracle pour des vérifications ponctuelles.
  2. Activez un chemin de repli logiciel (si disponible) et comparez. La divergence implique le matériel ou la génération de code bas niveau.
  3. Verrouillez le mode d’arrondi et les sous‑normales. Un environnement FP incohérent produit des différences « fantômes ».

Petite blague #2 : Si votre postmortem contient « les virgules flottantes, c’est bizarre », vous n’avez pas trouvé la cause—vous avez trouvé une excuse.

Trois mini-récits d’entreprise depuis les tranchées de la correctitude

Mini-récit 1 : L’incident causé par une mauvaise hypothèse

Un fintech de taille moyenne exécutait des calculs de risque quotidiens sur un cluster en batch. Le job était « embarrassingly parallel » : découper les portefeuilles, calculer les métriques, fusionner les résultats. Pendant des années, cela a fonctionné. Puis ils ont mis à jour un sous-ensemble de nœuds—CPU plus récents, même image OS, même digest de conteneur. Ils ont supposé « le calcul, c’est le calcul ».

Une semaine plus tard, la réconciliation a commencé à dériver. Pas catastrophiquement. Pensez petites différences sur des millions de comptes, du type qui ressemble au bruit d’arrondi jusqu’à ce que ce ne le soit plus. L’astreinte a passé en revue les suspects habituels : fuseaux horaires, formatage décimal, une nouvelle source de données. Rien.

La percée est venue quand quelqu’un a relancé la même tranche de portefeuille sur deux nœuds différents et a obtenu des valeurs divergentes à la 10ᵉ–12ᵉ décimale, suffisantes pour faire basculer un test de seuil en aval. La cause racine n’était pas un défaut matériel FDIV moderne ; c’était un changement dans les chemins de code en virgule flottante : les CPU plus récents avaient activé FMA, les anciens non, et le backend BLAS a choisi des chemins différents en conséquence.

La mauvaise hypothèse était simple : « Si le conteneur est identique, les calculs sont identiques. » Les conteneurs empaquettent l’espace utilisateur, pas le comportement du silicium. Leur correction a été opérationnelle : étiqueter les nœuds par capacités CPU, ordonnancer les jobs de risque sur une classe cohérente, et ajouter une étape de validation croisée qui comparait un échantillon à une référence haute précision.

La leçon FDIV s’applique : le matériel fait partie de votre surface d’API. L’ignorer et vous déboguerez votre propre confiance.

Mini-récit 2 : L’optimisation qui s’est retournée contre eux

Une équipe plateforme ML avait un problème de coût : l’inférence coûtait cher, et le produit voulait réduire le p95. Un ingénieur a activé un switch de compilateur agressif : optimisations flottantes agressives, plus une reconstruction du BLAS optimisée pour le jeu d’instructions le plus récent du parc.

La latence a baissé. Tout le monde a célébré. Puis une alerte de monitoring modèle s’est déclenchée : dérive des distributions de prédiction pour un sous-ensemble étroit d’entrées. Pas de crash. Pas de pic. Un biais silencieux et persistant. Le modèle n’avait pas changé ; le comportement numérique l’avait.

L’« optimisation » a altéré l’associativité et le comportement d’arrondi suffisamment pour déplacer certaines classifications limites au‑delà d’un seuil. La plupart des prédictions étaient identiques. Celles qui comptaient—les cas limites à haute valeur—ne l’étaient pas.

Ils ont annulé le build, réexécuté un corpus d’entrées représentatif, et institué une règle : toute optimisation numérique requiert un budget d’exactitude et une suite de régression incluant des tests de sensibilité aux seuils, pas seulement l’erreur moyenne. Ils ont aussi consigné quelles charges pouvaient tolérer les transformations non IEEE et lesquelles ne le pouvaient pas.

FDIV n’est pas survenu parce que quelqu’un a activé un flag. Mais le mode d’échec corporatif est le même : traiter la correctitude comme une qualité négociable. Ce n’en est pas une dans les parties du système qui font des engagements.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Un groupe de calculs de recherche exécutait de longues simulations sur un cluster partagé. Ils avaient une politique agaçante : chaque release devait produire une « empreinte numérique » sur un jeu de données de référence à graine fixe, stockée avec les métadonnées de l’artéfact de build. Même commit, même graine, même classe d’environnement, même plage de hache de sortie.

Un mois, un nouveau lot matériel est arrivé. Les jobs tournaient plus vite, mais l’empreinte a bougé. Pas beaucoup—juste de quoi dépasser leur enveloppe tolérée. Aucun utilisateur ne s’était encore plaint, parce que les sorties semblaient plausibles. Mais la politique l’a détecté avant de générer des résultats destinés à publication.

La cause était un changement runtime subtil : comportement différent de libm combiné avec un traitement par défaut distinct des nombres dénormalisés. Ce n’était pas un défaut de silicium spectaculaire, mais l’issue aurait pu coûter la réputation et des semaines de calcul perdu.

La correction a été procédurale et ennuyeuse : standardiser l’environnement FP, épingler les runs critiques à une classe de nœuds validée, et garder la porte de l’empreinte. Ils ont aussi documenté la variance numérique acceptable par type de modèle. La leçon : les gardes‑fous ennuyeux sont ce qui vous rend le sommeil.

Erreurs courantes : symptôme → cause racine → correction

Voici les motifs qui font traîner les bugs de correctitude pendant des mois. Traitez‑les comme des signatures d’échec.

1) Symptom: “Only one customer sees it”

Cause racine : Les entrées dépendantes de la charge/données déclenchent un motif d’opérande rare ou un bord de seuil.

Correction : Capturez les entrées exactes du client et rejouez‑les sur deux environnements. Ajoutez une validation canari pour cette classe d’entrées.

2) Symptom: “Staging can’t reproduce production”

Cause racine : Stepping CPU, microcode, ou différences d’instructions entre environnements. Les conteneurs ne résolvent pas ça.

Correction : Construisez des classes d’environnement (labels) et exigez que le staging corresponde à la classe production pour les tests de correctitude.

3) Symptom: “It’s random; rerun fixes it”

Cause racine : Courses de données, mémoire non initialisée, comportement indéfini, ou ordre non déterministe dans des réductions parallèles.

Correction : Utilisez des sanitizers, exécutez en mode référence mono‑thread, et imposez des réductions déterministes là où la correctitude importe.

4) Symptom: “The discrepancy is tiny, so it’s fine”

Cause racine : De petites différences numériques peuvent faire basculer des comparaisons, des seuils ou des branches, générant de grands effets aval.

Correction : Identifiez les frontières de seuil et ajoutez de l’hystérésis, des comparaisons epsilon, ou des calculs en plus haute précision pour les points décisionnels.

5) Symptom: “It started after a performance improvement”

Cause racine : fast-math, activation FMA, différences de vectorisation, ou changement de backend BLAS.

Correction : Maintenez deux profils de build (strict vs rapide). Verrouillez le profil rapide par des tests de régression numérique et un budget d’erreur documenté.

6) Symptom: “It happens only on one rack/region”

Cause racine : Différences de lot matériel, problèmes thermiques provoquant des comportements marginaux, ou BIOS/microcode différents.

Correction : Récupérez l’inventaire CPU/microcode, vérifiez les logs MCE, et mettez en quarantaine le matériel suspect. Ne discutez pas avec la physique.

7) Symptom: “Checksums pass, but analytics drift”

Cause racine : Votre pipeline de données est intact ; le calcul est faux. L’intégrité du stockage ne garantit pas l’intégrité du calcul.

Correction : Ajoutez une validation bout en bout : recalculer des échantillons sur une implémentation de référence reconnue et comparer.

Listes de contrôle / plan étape par étape

When you suspect a CPU-level correctness issue

  1. Geler les entrées : capturez les payloads exacts, les graines et la config. Pas de « à peu près ».
  2. Reproduire deux fois sur le même hôte. Si cela change d’un run à l’autre, vous poursuivez probablement de la nondéterminisme, pas du silicium.
  3. Reproduire sur un hôte différent avec une signature CPU différente. Si une classe d’hôte diverge, vous avez une prise pour segmenter.
  4. Empreinter la plateforme : modèle/stepping CPU, microcode, noyau, libc/libm, digest de conteneur, version du compilateur.
  5. Vérifier avec une référence : haute précision ou implémentation alternative pour un échantillon représentatif.
  6. Contenir : affecter les workloads sensibles à une classe matérielle validée ; drainer les nœuds suspects.
  7. Communiquer : les incidents de correctitude nécessitent un message clair aux parties prenantes—ce qui est impacté, ce qui ne l’est pas, et comment vous le savez.
  8. Remédier : remplacer/retraire le matériel si impliqué, ou standardiser runtime/flags si induit par le logiciel.
  9. Prévenir la récurrence : ajouter des portes d’empreinte numérique et un ordonnancement par classe d’environnement.

What to standardize in a production compute fleet (if you like sleeping)

  • Inventaire CPU suivi comme une dépendance : modèle, stepping, microcode.
  • Nœuds « golden » pour les jobs critiques pour la correctitude.
  • Profils de build : profil IEEE strict pour les workloads d’intégrité ; profil rapide uniquement avec approbation explicite.
  • Harnais de validation pouvant être exécutés à la demande : comparaisons croisées, contrôles de référence sur échantillons.
  • Métadonnées de provenance : étiquetez les résultats avec la classe d’hôte, l’ID de build et les versions de bibliothèques pour la traçabilité médico-légale.

FAQ

1) Was the Pentium FDIV bug a software bug or hardware bug?

Matériel. C’était un défaut d’implémentation dans la logique de division de la FPU du Pentium impliquant des entrées de table incorrectes utilisées lors de la division.

2) How common were wrong results?

Rares pour les charges grand public typiques, plus plausibles dans du code numérique intensif qui effectue beaucoup de divisions en virgule flottante avec des opérandes variés. « Rare » reste inacceptable quand l’erreur est silencieuse.

3) Could you fix FDIV with a software patch?

On pouvait parfois contourner par logiciel en évitant certains motifs d’instructions matérielles ou en utilisant une émulation logicielle de la division, mais la vraie correction a été le remplacement des puces affectées. Le défaut était dans le silicium.

4) Why didn’t normal testing catch it?

Parce que l’espace d’entrées en virgule flottante est énorme, et qu’une vérification fondée sur des « cas typiques » passe à côté des motifs d’opérandes rares. De plus, la pression de performance pousse historiquement les conceptions vers des approximations difficiles à valider exhaustivement.

5) What’s the modern equivalent risk?

Toute source de corruption silencieuse des données : matériel marginal (mémoire, CPU), comportement indéfini dans le code, flags de compilateur mathématiques agressifs, environnements FP incohérents sur des flottes hétérogènes, et bugs dans les bibliothèques numériques.

6) How do I protect a distributed system from silent math errors?

Utilisez la redondance et la validation : recalculer des échantillons sur des classes d’hôtes séparées, comparer des implémentations indépendantes, conserver des profils de build stricts pour l’intégrité, et marquer les sorties avec la provenance pour la traçabilité médico-légale.

7) Do GPUs have “FDIV-class” problems too?

Ils peuvent en avoir. Les GPU et accélérateurs peuvent utiliser des modes mathématiques différents (approximations rapides, opérations fusionnées, comportement flush-to-zero). Si vous avez besoin d’une reproductibilité stricte, vous devez configurer et tester en conséquence.

8) Is “fast-math” always wrong?

Pas toujours. C’est un compromis : vous autorisez des transformations qui peuvent violer les attentes IEEE. C’est acceptable pour certaines charges (graphisme, certaines inférences ML), dangereux pour d’autres (finance, publications scientifiques, cryptographie, pistes d’audit).

9) What should incident response look like for correctness bugs?

Comme un incident de sécurité : geler les preuves, limiter l’impact, contenir par classe d’environnement, communiquer clairement, puis seulement optimiser. La correctitude est une propriété d’intégrité, pas un indicateur de performance.

Étapes pratiques suivantes

Le bug Pentium FDIV n’est pas qu’une anecdote rétro sur l’informatique. C’est une leçon opérationnelle durable : les défaillances de correctitude ne se signalent pas, et « rare » n’est pas synonyme de « sûr ». Si vous exploitez des systèmes de production où les nombres deviennent des décisions, vous avez besoin d’un plan qui traite l’intégrité du calcul comme une préoccupation de fiabilité de premier ordre.

  1. Inventoriez votre parc de calcul par modèle/stepping CPU et microcode ; suivez‑le comme une dépendance.
  2. Créez des classes de nœuds validées et ordonnez les workloads critiques pour la correctitude dessus de façon intentionnelle.
  3. Établissez un profil de build mathématique strict et bannissez par défaut les flags non sûrs.
  4. Ajoutez une porte d’empreinte numérique pour les jobs clés : inputs à graine fixe, enveloppe tolérée, métadonnées de provenance.
  5. Construisez un runbook d’astreinte pour la correctitude qui commence par une comparaison cross-host et se termine par la contention, pas la spéculation.

Si vous n’optimisez que pour la vitesse, vous finirez par déployer des absurdités rapides. Le bug FDIV a rendu publique cette leçon. Vous n’avez pas besoin d’une humiliation mondiale pour l’apprendre en privé.

← Précédent
Drapeaux de fonctionnalités ZFS : règles de compatibilité entre hôtes et versions
Suivant →
Contrôleurs mémoire intégrés : le changement que vous ressentez encore aujourd’hui

Laisser un commentaire