# Tests infra — où HTP/HMX échoue, et pourquoi (29/05) Session de tests systématiques sur les hypothèses non-explorées du Phase 2b/F. Verdict global : **le HTP n'est pas le bon outil pour notre TTS**, parce que tous nos workloads sont des decodes batch=1 (talker, CP sub-forwards) ou des matmul énormes (BigVGAN) au-delà de la limite backend `nrows > 1024`. ## A — Sched HMX vs HVX (USE_HMX=0) Bench : CP + sched HTP, varier `GGML_HEXAGON_USE_HMX`. | Config | CP/frame | N frames | RTF | WAV md5 | |---|---:|---:|---:|---| | Baseline CPU pur | 110 ms | 33 | 2.92 | unique | | Sched HMX=1 | 226 ms | 64 | 4.48 | A2=A3 | | Sched HMX=0 (HVX) | 180 ms | 64 | 3.81 | A2=A3 | - HVX 20% plus rapide que HMX sur CP (tile 32×32 sous-utilisé à batch=1). - HMX et HVX **produisent bit-exact le même WAV**. - Les deux divergent du CPU baseline : N passe de 33 à 64 (le talker prend une trajectoire différente parce que les codes CP ne sont plus bit-exact à NEON). - Coût × 2 frames + overhead par-step = régression nette. **Conclusion A** : HVX > HMX sur batch=1, mais aucun ne bat NEON. ## B — op_offload=false vs true | Config | CP/frame | |---|---:| | HMX + op_offload=true | 216 ms | | HMX + op_offload=false | 181 ms | - Placement strict (false) = -16% sur CP. Moins de "magic" de placement adaptatif. - Toujours plus lent que baseline CPU. **Conclusion B** : `op_offload=false` est meilleur défaut. Quick win mais ne sauve pas la régression. ## C — Profile fin par sub-step (KZTTS_CP_PROFILE) Breakdown moyen d'un sub-forward (HVX) : | Étape | Temps | |---|---:| | `sched_reset + sched_alloc_graph` | 0.1 ms | | `ggml_backend_tensor_set` ×3 (x, pos_ids, mask) | 0.0 ms (< µs) | | **`sched_graph_compute`** | **6-11 ms (croît avec pos_off)** | | `ggml_backend_tensor_get` | 0.0 ms | - **L'overhead sched est négligeable** : ~0.1 ms/step × 15 = 1.5 ms/frame. Les copies CPU↔HTP sont gratuites (opt_hostbuf=1 = ION partagé). - **Le compute lui-même domine** : 95% du temps. - CPU NEON équivalent : ~7 ms/step, HVX 10 ms/step. HVX est intrinsèquement **plus lent que NEON sur ces matmul batch=1**. **Conclusion C MAJEURE** : ma hypothèse #1 (`sched_reserve` + ctx persistant) est invalidée. L'overhead qu'on espérait éliminer n'existe pas — le compute HTP est juste plus lent que CPU pour notre workload. ## D — Talker en HTP option C Pour vérifier si le pattern HTP-prefill / CPU-decode (validé pour Qwen3.5-4B) marche sur Qwen3-TTS-Talker (0.6B). | Métrique | CPU pur | HTP option C | |---|---:|---:| | Prefill | 87 ms | 185 ms (**+112%**) | | Talker per-frame | 32 ms | 81 ms (**+154%**) | | N frames | 33 | 18 | - Le talker TTS (0.6B) **REGRESSE sévèrement** en HTP option C. - Pour un petit modèle, l'overhead routing HTP par token (DMA, sync, sched inter-backend) dépasse le gain compute. - Le pattern option C ne marche bien que pour des modèles assez gros pour amortir le routing (Qwen3.5-4B = OK ; Qwen3-TTS 0.6B = perte). **Conclusion D** : le HTP n'apporte rien sur ce talker non plus. ## Verdict cumulé Le HTP est non-rentable sur **tous** nos workloads TTS : | Composant | Workload | HTP gain réel | Raison | |---|---|---|---| | BigVGAN | conv1d batch ≥ 60k | ❌ rejeté | limite backend `nrows > 1024` | | CP sub-forwards | matmul batch=1 ou 2 | ❌ régresse | compute HVX < NEON à batch=1 | | Talker decode | batch=1 | ❌ régresse | overhead routing > gain compute (0.6B) | | Talker prefill | batch ~10-20 | ❌ régresse | trop petit pour amortir | Le projet l'avait noté en partie dans CLAUDE.md : > « CPU NEON bat le HVX 3.4× sur Q6_K M=1 » > « decode CPU verrouillé (~16.5 tok/s), HTP perdant decode (~3.6 tok/s) » Mais on n'avait pas tiré la conséquence globale : **notre TTS n'a pas de workload "prefill HTP-friendly"**. Les phrases courtes (10-20 tokens) sont juste assez courtes que le routing coûte plus que le gain. ## Vrais leviers restants (sans HTP) Avec le HTP exclu, les optims réelles sont : 1. **Streaming pipeline** (1 session) — chevaucher talker / CP / decoder par chunks. Le RTF total ne baisse pas mais le TTFB perçu passe de ~5 s à ~500 ms. **Le seul gain UX significatif réaliste.** 2. **Réduction overhead CP CPU** (½ session) — réutiliser le ctx ggml entre les 15 sub-forwards par frame. L'overhead par-step est petit (~5 ms d'init/free × 15 = ~75 ms) mais réel. Estimé -10 à -15% sur CP CPU. 3. **Decoder : optim NEON conv1d** (R&D) — actuellement compute-bound à 95% du plafond NEON. Pour gagner faut vectoriser plus agressivement ou restructurer les conv1d en blocked matmul. Sujet R&D. 4. **Quantization plus agressive du CP** (½ session) — Q8 ou Q4 sur les poids matmul. Note CLAUDE.md : « Q8_0 CP perdu (76% codes vs 100% f16) » = qualité dégrade. Donc compromis qualité/perf à explorer. ## Ce qui est DÉJÀ committé qui marche - `KZTTS_CP_HTP=1` (sans SCHED) : poids CP sur HTP buffer, compute CPU pur. Neutre en perf, prouve que la chaîne marche. - `KZTTS_DECODER_HTP=1` (sans SCHED) : idem decoder. Neutre. - Phase 2b refactor stage_bigvgan_sched : code propre, désactivé par défaut. - Phase F refactor cp_forward_cached_step sched-friendly : idem. Tout ça est opt-in et n'affecte pas le path par défaut (RTF 3.0). ## Recommandation finale Le ROI / risque dit clairement : **streaming pipeline (#1) est le seul vrai gain accessible**. RTF<1 sur HTP demande de modifier le backend hexagon (semaines), ce qui sort du scope. Si Richard accepte RTF=3 mais TTFB=500 ms comme livrable acceptable, le chemin est clair en 1-2 sessions. Sinon, le frontier kernel HVX BigVGAN reste sur la table mais c'est un autre projet.