MIDDLEDAO · EBIN

ebin-bench — Bench eBin : A/B alterné anti-biais thermique, seuils, CV. — docs

ebin-bench — Méthodologie de benchmark fiable (crate eBin)

> Les seuils et outils ci-dessous sont ceux du crate eBin (repo 1000-dev-evo ; app canonique 2000-miniTools/130-ebin). Pour le

> benchmark des apps MiddleDAO (Hermes, Livia, OpenFang), voir `app-bench`

> (tableau KPI agents + protocole gradué).

Problème fondamental

Les benchmarks sur Mac M3 (ARM64 UMA) sont extrêmement sensibles à l'état thermique du CPU. Exécuter tous les benchmarks d'une branche puis tous ceux d'une autre introduit un **biais thermique** : la première branche bénéficie d'un CPU froid, la seconde subit un CPU chaud.

**Conséquence** : des écarts de -50% à +200% qui sont entièrement du bruit thermique, pas des vraies régressions.

Méthode A/B alternée (OBLIGATOIRE pour toute comparaison)

### Principe

Alterner les branches : `dev → cnt → dev → cnt → dev`. Jamais exécuter 2 runs consécutifs de la même branche.

### Template d'exécution

# execute_code — A/B alterné, 5 mesures
def run_bench(branch, bench_name):
    r = terminal(
        f"cd ~/MIDDLEDAO/1000-dev-evo && git checkout {branch} 2>&1 >/dev/null && "
        f"COLD_FORGE_BOOTSTRAP=1 cargo test -p evoBin --release --lib '{bench_name}' -- --nocapture 2>&1 "
        f"| grep 'ops/sec'",
        timeout=120
    )
    m = re.search(r'(\d+)\s+ops/sec', r["output"])
    return int(m.group(1)) if m else 0

results = {"dev": [], "dev-cnt": []}
for i in range(5):
    branch = "dev" if i % 2 == 0 else "dev-cnt"
    v = run_bench(branch, "stress_10_multithread_ring")
    results[branch].append(v)

dev_mean = sum(results["dev"]) / len(results["dev"])
cnt_mean = sum(results["dev-cnt"]) / len(results["dev-cnt"])
delta = (cnt_mean - dev_mean) / dev_mean * 100

### Interprétation

Delta (A/B alterné)Verdict
±5%Bruit statistique — pas de régression
±5-10%Marge acceptable — surveiller
±10-20%Régression possible — investiguer
>±20%Régression probable — cause structurelle

Variance d'un seul benchmark (CV)

Pour un benchmark isolé (pas de comparaison), lancer **10 runs consécutifs** et calculer le coefficient de variation :

CV = (std_dev / mean) × 100%
CVVerdict
<10%Mesure fiable
10-20%Variance modérée — moyenne fiable
20-40%Bruit thermique — résultats non-interprétables
>40%Benchmark flaky — ne pas tirer de conclusions

**Choisir le protocole AVANT d'exécuter** : comparaison de branches/conditions → A/B alterné ; bench mono-branche (baseline, signature métrologique) → protocole CV. Un run ×2 « bit-exact » prouve le DÉTERMINISME, pas la variance : c'est un critère R01, jamais un substitut au CV — deux runs ne donnent aucun coefficient. Toute baseline chiffrée livrée sans CV est une claim non qualifiée. CV calculé en entiers purs (sd via somme des carrés, zéro float).

### Cas réel : AtomicU64 store

Session 2026-08-07, 10 runs consécutifs :

- Mean: 12.9M, StdDev: 5.3M, **CV: 41.0%**

- Min: 6.2M, Max: 21.1M (variation de 3×)

- **Verdict** : bruit thermique pur, aucune conclusion structurelle possible

Bench hot-path 130-ebin (hotpath_bench, 09-28 — mini-tests ≤5 min)

`05-addins/8-bench/src/hotpath_bench.rs` : bench get/set du hot-path RÉEL (garde

RateLimiter incluse, n=32). Verdicts mesurés (Mac M3, release — les seuils ebin-bench

sont des seuils RELEASE ; dev opt-level=0 = 7,1 M seulement) :

MesureValeurVerdict
get (zéro-copie mmap)173,5 M ops/s release (seuil >50M ✅)120,9 M en A/B alterné ×3 (CV ~3 %)
set (écriture en place + flush)24,7 k ops/sflush-bound ~40 µs = durabilité, honnête
ancien evoBin bench_memcpy_12889,3 M ops/s (CV ~1 %)bench COPIE — protocole différent du get zéro-copie

Leçon : comparer ancien (bench copie) vs nouveau (bench zéro-copie) = +36 % apparent,

cause STRUCTURELLE (le get ne copie pas) — déclarer le protocole, ne jamais vendre le

chiffre comme un A/B strict. Absent chez l'ancien : bench get-strate comparable (CRUD

= opcodes MOE).

ORIENTATION HORIZONTALE (dur, user 09-29)

- Tout tableau comparatif (ancien vs nouveau, variantes) : **sujets comparés en COLONNES, métriques en LIGNES** — un sujet par ligne = non conforme, refaire.

- Ex : `| Métrique | ancien eBin | nouveau eBin (SEGx) | nouveau (TauView) |` — le nouveau grandit à droite.

Banc ANCIEN vs NOUVEAU, get COMPARABLE (09-29, forge `80-bench/strate_get.rs` commit 16b6061)

- get arène ancien (index direct) ≈ 5,6 G ops/s · get-par-id ancien (ColdSeg bit-math)

≈ 1,28 G ops/s (médiane — variance brute 0,88–3,95 G, turbo Mac) · get nouveau = 121,7 M

- **Verdict honnête : l'ANCIEN gagne le get ×10 (H2 falsifiée)** — id=adresse (R13) sans

index vs BTreeMap+mmap. Le nouveau achète pour ce coût : persistence conteneur disque,

seal/crypto ChaCha, layouts déclaratifs, format 08-ebin composite.

Banc TAUVIEW n=1024 (09-29, 09:07 EDT — release, 5 runs)

- V1 doctrinal 319 M (CV ~6 %) · **V2 tau-guard = TauView 3 228 M (CV <0,3 % — le plus

déterministe)** · V3 arène ancienne 1 208 M (CV ~7 %). TauView = ×10,1 vs V1, ×2,7

vs arène ancienne. Machine chaude : les 3 bras baissent ensemble (turbo), l'ÉCART s'élargit.

- Soluce get-perf CLOSE : tout l'outillage coldCompute applicable est appliqué ; le coût

restant (mmap file vs arène RAM) = choix d'architecture (durabilité), pas un outil manquant.

- Déclarer TOUJOURS le chemin exact (arène RAM vs mmap file ; index implicite vs lookup).

Leçon VARIANCE set (09-29) + seuils profil-aware

- **set = FLUSH-BOUND à variance élevée** : 24,7 k vs 12,8 k ops/s observés (spread ~48 %

> 40 % = non-interprétable en absolu, protocole CV). La métrique FIABLE du hot-path

eBin = **get**. Seuil set = sanité seulement (>5k). Cause : le flush mmap = I/O disque,

sensible à l'état du cache FS.

- **Seuils profil-aware** : les seuils du skill (get >50M) sont RELEASE ; en dev

(opt-level=0) le get tourne ~7-8M — jamais évaluer un bench dev avec des seuils release

(bench hotpath_bench.rs fait cfg!(debug_assertions)).

Erreurs classiques (à ne PAS reproduire)

ErreurConséquenceCorrection
Exécuter tous les benchmarks d'une branche puis l'autreBiais thermique -50% à +200%A/B alterné
Tirer des conclusions d'un seul runDonnées non fiablesMinimum 3 runs, idéalement 5 A/B
Comparer branches avec toolchains différentesLayout change avec Rust versionMême toolchain obligatoire
Attribuer régression au code alors qu'elle vient de la layoutMauvaise correctionVérifier taille binaire (nm, ls -la)
Ignorer CV >20% et présenter résultat comme factuelDécision basée sur bruitCalculer CV, rejeter si >20%

Taille binaire comme indicateur

Quand une régression apparaît après l'ajout de code :

ls -la target/release/evoBin
nm target/release/evoBin | wc -l

Si la différence est <0.1%, la régression vient de la **layout** (repositionnement des fonctions dans `.text`), pas du code ajouté. Solution : `#[cfg(feature = "...")]` pour exclure le code non-utilisé.

Feature gate pour layout control

Quand un module ajouté au binaire déplace les hot-loops et cause des régressions multi-thread :

# Cargo.toml
[features]
default = []
midos = ["regex-lite"]
// lib.rs
#[cfg(feature = "midos")]
pub mod midos;
# Containerfile (production uniquement)
RUN cargo build --release --features midos

**Effet** : le binaire par défaut (bench/dev) n'inclut pas le module → layout intact. Le binaire container inclut le module → fonctionnalité complète.

**Résultat mesuré** : CV du ring P+E-Core passe de 20.3% (midos inclus) à 6.4% (sans midos). Binaire : 80B de différence (0.008%).

Seuils anti-régression (Mac M3 ARM64)

MétriqueSeuilSource
AtomicU64 load>40M ops/sbenchMacro
AtomicU64 store>25M ops/s (CV <15%)benchMacro
XOR resonance>50M ops/sbenchMacro
memcpy 128B>50M ops/sbenchMacro
TETRA_ADD 8 threads>150M ops/sbenchStress
Ring P+E-Core>30M ops/sbenchStress
NOP stability 10M>20M ops/sbenchStress

Seuils (server3 i7-4770 x86_64)

MétriqueSeuilNotes
AtomicU64 load>50M ops/sRDTSC haute résolution
TETRA_ADD 8 threads>100M ops/s
Ring P+E-Core>25M ops/s

Observabilité hot-path — MdaoMetrics & coldcompute_effect

### MdaoMetrics — métriques d'observabilité hot-path

Commit `3d6b9ad` : `MdaoMetrics` câble des compteurs d'observabilité **directement dans le hot-path** du runtime eBin. Contrairement aux benchmarks ponctuels (benchMacro, benchStress), MdaoMetrics collecte des métriques en continu pendant l'exécution normale.

**Usage** : détecter des dégradations progressives (memory leaks, contention croissante) que les benchmarks A/B alternés ne capturent pas forcément.

### KPIs coldCompute dans le hot-path

Commit `154c147` : les indicateurs clés de performance (KPIs) de coldCompute sont câblés dans le hot-path via MdaoMetrics. Cela permet de surveiller en continu les métriques spécifiques à coldCompute (densité, calibration, supraconductivité) sans interrompre le flux d'exécution.

### Bench coldcompute_effect — indice d'effet A/B

Commit `b8af03f` : le bench `coldcompute_effect` mesure l'**indice d'effet** de coldCompute via une comparaison A/B. Il quantifie l'impact réel de coldCompute sur les performances en alternant états activé/désactivé, appliquant la même méthodologie anti-biais thermique que les benchmarks classiques.

**Format de sortie réel** (`coldcompute_effect.rs`, report) :

[ EFFECT ] nom_primitive      : cold=NNNNN  classic=NNNNN  gain=X.X× ✅/⚠️

Le gain est un ratio `classic/cold` en cycles (≥1.0 = ✅ coldCompute gagne). Primitives comparées : equator (AND vs branch), tetra (SHR vs DIV), fast_modulo (AND vs %), etc. — 1000 itérations via QuartzTuner::rdtsc, `#![deny(clippy::float_arithmetic)]` dans le bench.

Complémentarité des trois outils

Ces trois outils complètent la méthodologie de benchmark A/B alterné :

- **Bench A/B alterné** : mesure ponctuelle, comparaison de branches, anti-biais thermique.

- **MdaoMetrics + KPIs coldCompute** : observabilité continue du hot-path en production/développement.

- **coldcompute_effect** : mesure A/B de l'effet spécifique de coldCompute, pas de branches entières.

Ensemble, ils forment un système complet : bench pour les régressions ponctuelles, metrics pour la surveillance continue, effect pour quantifier l'impact d'un sous-système.