dist: prefill HTP RÉCUPÉRABLE — charabia = 1 op (SSM_CONV) cassée, fix OPFILTER

Investigation détaillée: décomposé le prefill HTP charabia op par op (test-backend-ops -b HTP0
par op + OPFILTER bisect en génération). Coupable unique = SSM_CONV (conv1d) cassé sur HTP en
multi-token (prefill) pour d_inner=1024/2048 (oracle FAIL ERR~1.3; decode n=1 OK; 1536 passe;
bug backend hexagon, HVX+scalaire HTP faux, ggml-cpu correct). FIX immédiat: GGML_HEXAGON_OPFILTER
=SSM_CONV (conv sur CPU, reste HTP) -> prefill HTP 181 t/s + sortie FR cohérente, x13 vs CPU 14.
Renverse le "prefill HTP mort": il est récupérable. 3 configs (A CPU-only livrée / B mono-ctx
HTP+OPFILTER 181/6.4 / C dual-ctx 181/10.9 +2.3GB). TODO: corriger SSM_CONV HTP (gate oracle).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Richard Loyer 2026-05-27 10:12:37 +02:00
parent 2256c077c9
commit 501abb67e5
3 changed files with 27 additions and 21 deletions

28
dist/HANDOFF.md vendored
View File

@ -24,22 +24,26 @@ Config déjà correcte et alignée :
(40%, idem à d=512). Le déquant KV en flash-attn coûte plus que le BW épargné. `type_k/v` (40%, idem à d=512). Le déquant KV en flash-attn coûte plus que le BW épargné. `type_k/v`
repassé en `GGML_TYPE_F16`. → **rebuild `libkazeia_engine.so` avant de shipper** (le .so livré repassé en `GGML_TYPE_F16`. → **rebuild `libkazeia_engine.so` avant de shipper** (le .so livré
contient encore l'ancien q8_0). q8_0 seulement si OOM KV en très long contexte. contient encore l'ancien q8_0). q8_0 seulement si OOM KV en très long contexte.
2. **Prefill : CPU-only OBLIGATOIRE — le prefill HTP est CASSÉ (vérifié 27/05).** Le HTP a un gros 2. **Prefill HTP : RÉCUPÉRABLE via `GGML_HEXAGON_OPFILTER=SSM_CONV`.** Cause du charabia localisée :
débit (q35-lmq4 126-189 t/s) MAIS produit un **état/logits corrompus → sortie charabia**. Confirmé 3× **une seule op, SSM_CONV (conv1d), est cassée sur HTP en multi-token (prefill) pour d_inner=1024/2048**
(harness dual-contexte, device HTP0 explicite, et `llama-cli -dev HTP0` sortant de l'anglais cassé (oracle FAIL ERR~1.3 ; decode n=1 OK ; bug côté backend hexagon, pas le calcul — les 2 chemins HTP
sur un prompt FR). Le dense Qwen3-4B sur HTP **crashe** (V79 0x2e). Preuve que c'est le prefill et pas échouent, ggml-cpu est correct). En forçant cette op sur CPU (`OPFILTER=SSM_CONV`), le reste du prefill
le transfert : decode CPU repartant d'un état prefillé-CPU = FR cohérent ; repartant d'un état tourne sur HTP et la **sortie redevient cohérente** : « Je comprends que cette rumination nocturne… ».
prefillé-HTP = charabia. Donc les « 189/98/285 » sont du **débit jamais validé en sortie**, inutilisables. Mesuré : **prefill HTP 181 t/s** (×13 vs CPU 14) avec sortie correcte. Trois options d'intégration :
**JNI reste `ngl0` (CPU prefill 14 t/s + CPU decode 10.9).** Mécanique du split (2 contextes + transfert - **A (livrée) CPU-only** : prefill 14, decode 10.9, 2.4 GB. Simple.
KV) validée et prête (jni/dual_ctx.cpp) — réutilisable LE JOUR où le prefill HTP sera numériquement - **B mono-contexte HTP + OPFILTER** : prefill **181**, decode HTP **6.4**, 2.4 GB (+ION). Gros gain prefill,
correct (bug backend frontière). Pour accélérer le prefill maintenant : raccourcir le prompt/historique. decode + lent, aucune RAM en plus, peu de code (ngl99 + env). **Recommandé** pour prompts longs/multi-tour.
- **C dual-contexte (prefill HTP+OPFILTER → transfert KV → decode CPU)** : prefill 181, decode **10.9**,
**+2.3 GB** RAM (2e instance). Optimal mais + complexe. Harness prouvé : `jni/dual_ctx.cpp`.
TODO propre : corriger SSM_CONV HTP (gate `test-backend-ops -o SSM_CONV` = 45/45) → enlèverait l'OPFILTER.
## Perf (sains, 27/05, device froid) ## Perf (sains, 27/05, device froid)
| | prefill | decode | RAM | | | prefill | decode | RAM |
|---|--:|--:|--:| |---|--:|--:|--:|
| q35-lmq4 — JNI (CPU-only, **config livrée**) | 14 (CPU t4) | **10.9** (t4+fa, KV f16) | 2.4 GB | | q35-lmq4 — A: CPU-only (**config livrée**) | 14 (CPU t4) | **10.9** | 2.4 GB |
| q35-lmq4 — HTP | 126-189 *(débit, sortie CASSÉE → inutilisable)* | — | — | | q35-lmq4 — B: HTP+OPFILTER mono-ctx | **181** | 6.4 (HTP) | 2.4 GB +ION |
| dense Qwen3-4B | ~14 CPU / HTP crash 0x2e | 11.7 | 2.2 GB | | q35-lmq4 — C: HTP-prefill / CPU-decode | **181** | **10.9** | 4.7 GB +ION |
| q35-lmq4 — HTP sans OPFILTER | 189 *(sortie CASSÉE: SSM_CONV)* | — | — |
Decode = ce que l'utilisateur ressent (10.9, OK). Prefill CPU 14 t/s → prompt 200 tok ≈ 14 s ; Decode = ce que l'utilisateur ressent (10.9, OK). Prefill CPU 14 t/s → prompt 200 tok ≈ 14 s ;
garder le system prompt + l'historique courts. Le prefill HTP rapide existe mais sort du charabia (cf décision 2). garder le system prompt + l'historique courts. Le prefill HTP rapide existe mais sort du charabia (cf décision 2).

13
dist/PERF.md vendored
View File

@ -9,12 +9,13 @@ Decode optimum = **t4 / fa1 / KV f16** (PAS t8 : contention ; PAS KV q8_0 : déq
| Qwen3.5-9B | — | — | ~4-5 | 5.0 | | Qwen3.5-9B | — | — | ~4-5 | 5.0 |
Notes : Notes :
- **PREFILL HTP = CASSÉ (27/05, vérifié 3×).** Débit réel (126-189 t/s) mais **état/logits corrompus → - **PREFILL HTP charabia = 1 op cassée, SSM_CONV (conv1d), RÉCUPÉRABLE.** Localisé 27/05 : SSM_CONV sur HTP
sortie charabia** (harness dual-ctx, device HTP0 explicite, et `llama-cli -dev HTP0` → anglais cassé sur est faux en multi-token (prefill) pour d_inner=1024/2048 (oracle FAIL ERR~1.3 ; decode n=1 OK ; bug backend
prompt FR). Preuve = c'est le prefill, pas le transfert : decode CPU depuis état prefillé-CPU = FR cohérent, hexagon, pas le calcul — HVX ET scalaire HTP échouent, ggml-cpu correct). Fix immédiat = `GGML_HEXAGON_OPFILTER=SSM_CONV`
depuis état prefillé-HTP = charabia. Les anciens « 189/98/285 » = débit jamais validé en sortie, **inutilisables**. (route conv sur CPU, reste sur HTP) → **prefill HTP 181 t/s + sortie cohérente** (« Je comprends que cette
Dense sur HTP **crashe** (V79 0x2e). → **prefill obligatoirement CPU.** Le KV transfer (Path A) marche rumination… »), ×13 vs CPU 14. 3 configs : A CPU-only (14/10.9, livrée), B mono-ctx HTP+OPFILTER (181/6.4),
(CPU→CPU cohérent), prêt si le prefill HTP devient un jour correct (bug backend frontière). C dual-ctx prefill-HTP/decode-CPU (181/10.9, +2.3GB). cf HANDOFF.md décision 2. Dense sur HTP crashe (0x2e).
TODO: corriger SSM_CONV HTP (gate test-backend-ops -o SSM_CONV = 45/45) → supprime l'OPFILTER.
- **KV f16 ≫ q8_0 au decode** : q35-lmq4 f16=10.9 vs q8_0=6.5 (40%, idem à d=512). → JNI `type_k/v=F16`. - **KV f16 ≫ q8_0 au decode** : q35-lmq4 f16=10.9 vs q8_0=6.5 (40%, idem à d=512). → JNI `type_k/v=F16`.
- **GDN-compute n'est PAS le goulot prefill** (débit) : serial f32 / fp16 / WY = même pp512. Piste kernel GDN close. - **GDN-compute n'est PAS le goulot prefill** (débit) : serial f32 / fp16 / WY = même pp512. Piste kernel GDN close.
- NPU≈CPU au decode (GEMV M=1 latency-bound). RAM = GGUF mmap + ~3.3 GB ION/session. - NPU≈CPU au decode (GEMV M=1 latency-bound). RAM = GGUF mmap + ~3.3 GB ION/session.

7
dist/STATUS.md vendored
View File

@ -5,9 +5,10 @@ thinking-off déterministe. Modèle tranché = `q35-lmq4` (éval qualité ../eva
Perf : prefill clos côté kernel GDN (pas le goulot), decode au plafond CPU. Perf : prefill clos côté kernel GDN (pas le goulot), decode au plafond CPU.
LIMITES / OUVERT : LIMITES / OUVERT :
1. **Prefill HTP = CASSÉ (sortie charabia), prefill OBLIGATOIREMENT CPU (~14 t/s).** Vérifié 27/05 1. **Prefill HTP RÉCUPÉRABLE** (27/05) : charabia causé par 1 op, SSM_CONV (conv1d), cassée sur HTP en
(cli -dev HTP0 + harness). Le débit HTP est réel mais l'état produit est corrompu. Dense sur HTP crashe prefill. Fix = `GGML_HEXAGON_OPFILTER=SSM_CONV` → prefill HTP 181 t/s cohérent (×13). Config livrée = CPU-only
(0x2e). Bridge CPU-only (ngl0) = la seule config correcte. cf PERF.md / HANDOFF.md décision 2. (A) ; B = mono-ctx HTP+OPFILTER (181/decode 6.4) ; C = dual-ctx (181/decode 10.9, +2.3GB). cf HANDOFF.md déc.2.
TODO backend : corriger SSM_CONV HTP (oracle test-backend-ops -o SSM_CONV).
2. **KV = f16 (résolu, JNI corrigé)** : f16=10.9 vs q8_0=6.5 au decode. Rebuild .so requis (livré=q8_0). 2. **KV = f16 (résolu, JNI corrigé)** : f16=10.9 vs q8_0=6.5 au decode. Rebuild .so requis (livré=q8_0).
3. `generate()` applique déjà ChatML + thinking-off. App fournit `sys`/`usr` (voir system_fr.txt). 3. `generate()` applique déjà ChatML + thinking-off. App fournit `sys`/`usr` (voir system_fr.txt).
4. TTS Talker GGUF chargeable mais **audio e2e non validé** (= intégration app). 4. TTS Talker GGUF chargeable mais **audio e2e non validé** (= intégration app).