Kazeia-engine/CHANTIER_HMX_DENSE.md

76 lines
5.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Chantier — matmul HMX robuste pour le DENSE sur HTP (crash 0x2e)
Objet : faire tourner le dense (qwen3) sur HTP en **HMX** (prefill ~295) au lieu de HVX (~40), maintenant
que le `.pte` est retiré (l'engine est le seul runtime LLM). Bloqueur = crash du matmul HMX sur le dense.
## Symptôme
Prefill dense (qwen3-4B Q4_0) sur HTP avec HMX (défaut) → `ggml-hex: dspqueue_read failed: 0x0000002e`
dans `flush_pending``llama_decode`, dès que le prompt dépasse ~12-16 tokens. HVX (`GGML_HEXAGON_USE_HMX=0`)
ne crashe pas. La 3.5 (hybride) ne crashe pas en HMX.
## Repro
```
GGML_HEXAGON_OPLOG=1 GGML_HEXAGON_OPBATCH=1 LD_LIBRARY_PATH=./lib ADSP_LIBRARY_PATH=./lib \
./llama-cli -m Qwen3-4B-Q4_0.gguf -ngl 99 -dev HTP0 -t 4 --reasoning off -cnv -st --temp 0 -n 2 \
-p 'un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize'
```
## Hypothèse ÉCARTÉE : overflow fp16
Le matmul fautif a une **activation max-abs = 9.8** (mesuré via OPLOG `s1_amax`), très en-dessous de la plage
fp16 (65504). **Ce n'est PAS un overflow.** Confirmé : clamp ±65504 (conversion f32→f16) ET scaling activations
÷32 puis ÷256 (avec ×S en sortie) n'ont rien changé au crash. Donc ni l'entrée ni l'accumulateur fp16 ne débordent.
## Localisation (via instrumentation, voir plus bas)
Dernier op avant le 0x2e = un **MUL_MAT q4_0**, juste après `FLASH_ATTN_EXT` :
```
KZOP MUL_MAT dst=[2560,38,1,1] src0=q4_0[4096,2560] src1=f32[4096,38] s1_amax=9.8 <- CRASHE
```
C'est l'**o_proj** (projection de sortie d'attention : in 4096 = 32 têtes×128, out 2560 = hidden), donc **k=4096**.
Les autres MUL_MAT du graphe ont **k=2560** et passent (ffn gate/up `[2560,4096]`, qkv `[2560,1024]`).
**suspect : le chemin HMX pour k=4096** (n_dot_tiles = 4096/32 = 128). `OPFILTER=MUL_MAT` (matmuls→CPU)
supprime le crash, confirmant que c'est bien un MUL_MAT HMX.
## Instrumentation — ce qui marche / ne marche pas sur ce device (non-rooté)
-**FARF DSP** : bloqué. Le watcher cherche `<process>.farf` dans `/vendor/lib/rfsa/adsp` (lecture seule
sans root). `FARF(RUNTIME_HIGH)` ne sort pas en logcat sans ce fichier. `getenv` ne marche PAS côté skel DSP.
-**GGML_HEXAGON_VERBOSE** : utilise `GGML_LOG_DEBUG`, masqué par le cli.
-**OPLOG CPU-side** (committé) : `enqueue_op` (ggml-hexagon.cpp) logge en `fprintf(stderr)` chaque op
(type, dims, type/dims src, max-abs activation f32), gaté par `GGML_HEXAGON_OPLOG`. Combiné à
`GGML_HEXAGON_OPBATCH=1` (1 op par flush), le **dernier KZOP avant le 0x2e = l'op fautive**.
- Build debug FARF dispo si besoin : `cmake -DHEXAGON_HTP_DEBUG=ON` dans le sous-build htp-vNN → active
`FARF_HIGH=1` (logs par-matmul m/k/n), mais inutile ici faute de capture (root).
## Avancement (27/05, session chantier)
Hypothèses **éliminées** par mesure (OPLOG + isolation) :
- ❌ Overflow fp16 d'entrée : activation du matmul fautif amax=**9.8** (≪ 65504). Clamp ±65504 = sans effet.
- ❌ Overflow d'accumulation : scaling ÷32 puis ÷256 (×S en sortie) = sans effet.
- ❌ NaN/Inf dans les inputs : instrumentation `nan=0 inf=0` sur tous les ops avant le crash (le `amax` initial
ratait les NaN car `v>amax` est faux sur NaN ; corrigé pour les détecter → toujours 0).
- ❌ Shape pure : le matmul isolé (test-backend-ops, q4_0 k=4096 n=38/512/16 m=2560, **données synthétiques**)
**ne crashe PAS** sur HTP (finit, finite). Donc ce n'est pas la shape seule.
**Op fautive confirmée** : le **o_proj** `MUL_MAT q4_0 [4096,2560] × f32[4096,38]` (k=4096) juste après FLASH_ATTN_EXT
(en OPBATCH=1, le flush de ce matmul survient juste après le log de l'ADD suivant → crash). Inputs propres.
**Conclusion** : crash **value/context-spécifique** — un pattern de valeurs réelles (non reproduit par du random
synthétique), OU un état de contexte/VTCM/session hérité des ops précédentes (FA), qui fait fauter le MAC HMX.
## Prochaines étapes (révisées après l'avancement ci-dessus)
1. **Dump-and-replay** (tranche value-vs-context) : capturer la vraie activation `[4096,38]` + le poids q4_0
du o_proj fautif (CPU-side dans `enqueue_op`, écrire en fichier), puis rejouer EXACTEMENT ces octets dans un
harness qui lance `hmx_matmul_q_f32` sur HTP. Crash en replay → **value-pattern** (pattern de bits précis
qui faute le MAC/dequant) ; pas de crash → **context/état** (VTCM/session de la séquence d'ops, ex. FA avant).
2. **Si value-pattern** : trouver l'octet/valeur déclencheur (bisection sur les lignes de l'activation), inspecter
le dequant q4_0→fp16 (`dequantize_x4x2_*`) et le MAC (`core_dot_chunk_fp16`, `Q6_mxmem_*`) sur ce pattern.
3. **Si context** : instrumenter l'état VTCM/session avant le matmul (reset explicite entre ops ? interaction FA→matmul).
4. **Instrumentation DSP** (si nécessaire, FARF bloqué sans root) : ajouter dans le skel un **log-vers-buffer-hôte**
(écrire diagnostics dans une zone VTCM/DDR que le CPU relit) — contourne l'absence de capture FARF.
5. **Fallback pragmatique** si le fix est trop lourd : gater le HMX pour cette classe de matmul → HVX (comme le
gate SSM_CONV). Mais le dense est déjà 100% HVX (USE_HMX=0) côté JNI, donc le fallback = statu quo.
⚠ L'oracle `test-backend-ops -o MUL_MAT` a des FAIL `CPU=nan` PRÉ-EXISTANTS (réf CPU NaN sur petits q4_0) — non
fiable comme gate ; valider par la cohérence de génération + l'absence de crash, pas par cet oracle.
## État
**Non-bloquant** : le dense tourne déjà sur HTP en **HVX** (~40 prefill, stable, cohérent) via le routage
auto du JNI (`USE_HMX=0` pour le dense). Ce chantier = optimisation (40→295). 3.5 intacte (HMX + option C).
Outils committés : OPLOG (`GGML_HEXAGON_OPLOG`, dormant par défaut).