import re
print("=" * 70)
print("Cellule 7 — Mesures agrégées + périphérique effectif + comparaison Phase 1")
print("=" * 70)
def find(pattern, text, cast=str, default=None):
"""Relit une valeur DANS la sortie du run. Rend `default` si l'observable
est absent — un observable manquant ne se remplace jamais par une valeur
de référence collée : il se signale."""
m = re.search(pattern, text or "")
return cast(m.group(1).replace(",", ".")) if m else default
# --- Mesures LLamaSharp Phase 2, RELUES dans la sortie de la cellule 6 --------
llamasharp = {
"model": "Qwen3-4B Q4_K_M",
"backend": "LLamaSharp 0.27.0 (binding .NET llama.cpp)",
"host": ".NET 8.0.27 self-contained",
"device_requested": "CUDA 12 (RTX 3080 Ti, paquets Backend.Cuda12)",
"n_ctx": 2048,
"n_gpu_layers_requested": 99, # paramètre DEMANDÉ — ne prouve rien à lui seul
"total_tokens": find(r"Total tokens brut\s*:\s*(\d+)", batch_out, int),
"total_pad_tokens": find(r"Total <pad> jetons\s*:\s*(\d+)", batch_out, int),
"total_time_s": find(r"Time total\s*:\s*([\d.,]+)\s*s", batch_out, float),
"aggregate_tok_per_s": find(r"Rate agrégé brut\s*:\s*([\d.,]+)", batch_out, float),
"pad_ratio_pct": find(r"Pad ratio\s*:\s*([\d.,]+)", batch_out, float),
"per_request": [
{"tokens": int(t), "pad": int(p), "time_s": float(d), "tok_per_s": float(v)}
for t, p, d, v in re.findall(
r"Tokens:\s*(\d+),\s*Pad:\s*(\d+),\s*Time:\s*([\d.]+)s,\s*Rate:\s*([\d.]+)",
batch_out or "")
],
}
# --- Périphérique : observables RELUS dans les logs natifs de la cellule 5 -----
layers = re.search(r"offloaded\s+(\d+)\s*/\s*(\d+)\s+layers to GPU", smoke_out or "")
device_check = {
"load_tensors: couches offloadées": f"{layers.group(1)} / {layers.group(2)}" if layers else "non trouvé",
"backend_ptrs.size()": find(r"backend_ptrs\.size\(\)\s*=?\s*(\d+)", smoke_out, int, "non trouvé"),
"CUDA0 compute buffer (MiB)": find(r"CUDA0 compute buffer size\s*=\s*([\d.,]+)", smoke_out, float, "non trouvé"),
"graph splits": find(r"graph splits\s*=?\s*(\d+)", smoke_out, int, "non trouvé"),
"VRAM min → max (MiB)": (f"{min(vram_samples)} → {max(vram_samples)} "
f"(delta {max(vram_samples) - min(vram_samples)})") if vram_samples else "non échantillonnée",
}
llamasharp["device_effective"] = "GPU (CUDA0)" if layers and layers.group(1) != "0" else "à vérifier"
llamasharp["n_gpu_layers_effective"] = int(layers.group(1)) if layers else None
# --- Contrôle CPU du MÊME run, relu dans la sortie de la cellule 6bis ----------
# C'est cette colonne qui porte le rapport : elle a été produite par le même
# exécutable, dans la même minute, sur les mêmes invites. Un rapport calculé
# contre un chiffre d'un autre jour mesurerait aussi l'état de la machine.
couches_cpu = re.search(r"offloaded\s+(\d+)\s*/\s*(\d+)\s+layers to GPU", cpu_out or "")
llamasharp_cpu = {
"device_effective": "CPU" if (not couches_cpu or couches_cpu.group(1) == "0") else "GPU (inattendu)",
"n_gpu_layers_effective": int(couches_cpu.group(1)) if couches_cpu else 0,
"backend_ptrs": find(r"backend_ptrs\.size\(\)\s*=?\s*(\d+)", cpu_out, int, "non trouvé"),
"total_tokens": find(r"Total tokens brut\s*:\s*(\d+)", cpu_out, int),
"total_pad_tokens": find(r"Total <pad> jetons\s*:\s*(\d+)", cpu_out, int),
"total_time_s": find(r"Time total\s*:\s*([\d.,]+)\s*s", cpu_out, float),
"aggregate_tok_per_s": find(r"Rate agrégé brut\s*:\s*([\d.,]+)", cpu_out, float),
}
# --- Référence historique : le MÊME harnais AVANT la réparation du §1bis --------
# Citée comme mesure datée, jamais comme une sortie du run courant, et jamais
# comme terme d'un rapport : elle vient d'un autre jour.
llamasharp_cpu_avant = {
"date": "2026-08-24 (attribution corrigée le 2026-09-01)",
"total_tokens": 353, "total_time_s": 24.97, "aggregate_tok_per_s": 14.14,
"observables": "backend_ptrs.size() = 1, 0/36 couches, graph splits = 1, VRAM plate (min = max)",
}
# --- Observation datée : la variabilité inter-runs qui motive le contrôle -------
# Ces valeurs ne viennent PAS du run courant. Elles sont déclarées ici, datées et
# sourcées, plutôt que glissées dans une phrase : un nombre recopié à la main dans
# de la prose est exactement la dérive C.4 que ce notebook corrige par ailleurs.
variabilite_inter_runs = {
"date": "2026-09-02",
"runs_gpu_tok_per_s": [35.37, 123.43],
"dispersion_intra_run_pct": 3,
"cause": "occupation du GPU par un processus tiers (9442 MiB résidents)",
}
# --- Mesures TensorSharp Phase 1 (citées verbatim de la PR #12645 / notebook 10d) --
tensorsharp = {
"model": "Gemma 4 E4B Q8_0 (serveur distant)",
"backend": "TensorSharp CUDA (PR #12645)",
"device_effective": "GPU distant (non vérifié depuis ce notebook)",
"total_tokens": 159, # 160 générés mais 159 étaient <pad>
"total_pad_tokens": 159,
"aggregate_tok_per_s": "~50 (côté serveur)",
"pad_ratio_pct": 99.4,
}
print("\n--- Périphérique effectif (observables relus dans les logs) ---")
for k, v in device_check.items():
print(f" {k:34s} : {v}")
print("\n| Métrique | LLamaSharp GPU (ce run) | LLamaSharp CPU (ce run) | TensorSharp Phase 1 |")
print("|-----------------------|-------------------------|-------------------------|---------------------|")
print(f"| Périphérique EFFECTIF | {llamasharp['device_effective']:>23} | "
f"{llamasharp_cpu['device_effective']:>23} | {tensorsharp['device_effective']:>19} |")
print(f"| Couches sur GPU | {str(llamasharp['n_gpu_layers_effective']):>23} | "
f"{llamasharp_cpu['n_gpu_layers_effective']:>23} | {'n/a':>19} |")
print(f"| Tokens générés | {str(llamasharp['total_tokens']):>23} | "
f"{llamasharp_cpu['total_tokens']:>23} | {tensorsharp['total_tokens']:>19} |")
print(f"| Jetons <pad> | {str(llamasharp['total_pad_tokens']):>23} | "
f"{str(llamasharp_cpu['total_pad_tokens']):>23} | {tensorsharp['total_pad_tokens']:>19} |")
print(f"| Temps total (s) | {str(llamasharp['total_time_s']):>23} | "
f"{llamasharp_cpu['total_time_s']:>23} | {'n/a':>19} |")
print(f"| tok/s agrégé | {str(llamasharp['aggregate_tok_per_s']):>23} | "
f"{llamasharp_cpu['aggregate_tok_per_s']:>23} | {str(tensorsharp['aggregate_tok_per_s']):>19} |")
if llamasharp["aggregate_tok_per_s"] and llamasharp_cpu["aggregate_tok_per_s"]:
ratio = llamasharp["aggregate_tok_per_s"] / llamasharp_cpu["aggregate_tok_per_s"]
memes_jetons = llamasharp["total_tokens"] == llamasharp_cpu["total_tokens"]
print(f"\nAccélération GPU / CPU : ×{ratio:.2f}"
f" (même binaire, même minute, mêmes invites ; mêmes jetons : {memes_jetons})")
print(f" contrôle CPU : backend_ptrs.size() = {llamasharp_cpu['backend_ptrs']}, "
f"{llamasharp_cpu['n_gpu_layers_effective']} couche(s) sur GPU "
f"— le seul levier retiré est CUDA_PATH.")
print(f"\nRéférence datée du {llamasharp_cpu_avant['date']} : "
f"{llamasharp_cpu_avant['aggregate_tok_per_s']} tok/s en CPU "
f"({llamasharp_cpu_avant['observables']}).")
print(" Elle N'entre PAS dans le rapport ci-dessus : mesurée un autre jour, elle")
print(" porterait l'état de la machine autant que le périphérique.")
print("\nLecture :")
print("- Le sampling est déterministe (Temperature = 0.0) : à invites identiques, le")
print(" nombre de jetons l'est aussi. Les deux régimes portent donc exactement la")
print(" même charge, et le rapport ne compare que le périphérique.")
print(f"- Ce débit dépend de l'occupation du GPU. Observation datée du "
f"{variabilite_inter_runs['date']} : deux exécutions du MÊME binaire GPU, à")
print(f" quelques minutes d'intervalle, ont rendu "
f"{' puis '.join(f'{v} tok/s' for v in variabilite_inter_runs['runs_gpu_tok_per_s'])}")
print(f" — chacune homogène à ±{variabilite_inter_runs['dispersion_intra_run_pct']} % "
f"sur ses 4 requêtes, soit "
f"×{max(variabilite_inter_runs['runs_gpu_tok_per_s']) / min(variabilite_inter_runs['runs_gpu_tok_per_s']):.1f} "
f"entre elles.")
print(f" Cause : {variabilite_inter_runs['cause']}. C'est la raison d'être du")
print(" contrôle CPU intra-run : un rapport qui croise deux journées mesurerait")
print(" aussi la charge de la machine, et non le seul périphérique.")
print("- LLamaSharp et TensorSharp ne se comparent toujours PAS en débit : l'un est")
print(" in-process, l'autre derrière un serveur HTTP distant. Ce qui les sépare est la")
print(" qualité textuelle — 0 % de <pad> contre 99,4 %.")