Kazeia-engine/dist/PERF_ANALYSIS.md

173 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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.

# Analyse perf TTS Kazeia — 28/05 soir
État après les 4 chantiers de la session (RTF 4.99 → 3.07). Mesures sur Pad3,
CPU 4 threads talker / 8 threads decoder, KZTTS_CP_CACHE=1, phrase « Bonjour je m'appelle
Kazeia » → 31 frames audio (2.58s).
## 1. Breakdown précis (mesuré)
| Étape | Temps | %total | Par frame | RTF partiel |
|---|---:|---:|---:|---:|
| Prefill talker | 0.10 s | 1.3% | 3 ms | 0.04 |
| **Talker loop** (decode 31×1 token) | 1.00 s | 12.6% | 33 ms | 0.39 |
| **CP loop** (31× (1 prefill + 14 decode)) | 3.40 s | 43.0% | 110 ms | 1.32 |
| **Decoder** (5 stages) | 3.40 s | 43.0% | — | 1.32 |
| TOTAL | 7.90 s | 100% | | **3.06** |
**Decoder breakdown** (mesuré via `KZTTS_DECODER_PROFILE=1`) :
| Stage | Temps | % decoder |
|---|---:|---:|
| 1. Quantizer (16 embeddings lookup) | 18 ms | 0.5% |
| 2. Pre-conv (k=7) | 16 ms | 0.5% |
| 3. Pre-transformer (8 layers attn) | 30 ms | 0.9% |
| 4. Upsample (2 stages ×4) | 95 ms | 2.8% |
| **5. BigVGAN** (4 blocks + final, total ×480) | **3 240 ms** | **95.3%** |
**Conclusion immédiate** : 86% du pipeline est dans deux blocs (CP loop + BigVGAN).
Toute perf doit attaquer un de ces deux. Le reste est négligeable.
## 2. Pourquoi c'est lent (diagnostic)
### CP : 110 ms/frame, **BW-bound** sur ggml-cpu
15 sub-forwards par frame (1 prefill 2 tokens + 14 decode 1 token via KV cache).
Chaque sub-forward stream ~25 MB de poids f16 par layer × 5 layers = 125 MB.
À 77 GB/s LPDDR5x → 1.6 ms BW pur par sub-forward. **Mesuré 7 ms/sub-forward**, soit
~25% compute et ~75% overhead (ggml_init/free, graph build, alloc work buffers).
Confirmation BW-bound : thread sweep 4 vs 6 vs 8 → CP plat à ±5%. Le compute ne fait
pas la différence, c'est la mémoire qui plafonne.
### BigVGAN : 3.2 s, **compute-bound** sur ggml-cpu NEON
Estimation analytique : ~500 Gops sur le forward complet (conv1d k=7 dilatés +
convtranspose1d avec stride up_rates=[8,5,4,3]). Sur 8 threads NEON 4-wide
~150 GFlops total → ~3.3 s. **Cohérent avec la mesure (3.2 s)** → on est à ~95%
du plafond NEON. Pas de gain à attendre du multi-thread (déjà saturé).
Hot inner ops : `ggml_conv_1d` et `ggml_conv_transpose_1d` qui appellent
mul_mat NEON après im2col. Pas de path HVX/HMX dans ggml-cpu.
### Talker : 33 ms/frame, presque OK
llama.cpp standard decode 1 token. Pas un goulot. La piste HTP prefill talker existe
(`use_htp=true`) mais elle ne gagne que sur le prefill (0.1 s) — la boucle decode
reste CPU. Gain potentiel : <0.05 s. **Pas un levier.**
## 3. Plafonds théoriques par hardware
| Composant | Sur ce qu'on a | Plafond CPU NEON | Plafond HVX V79 | Plafond HMX V79 |
|---|---:|---:|---:|---:|
| CP loop | 3.4 s | ~3.0 s (BW-saturée) | ~1.5 s (×2.2) | ~0.6 s (×5.7) |
| BigVGAN | 3.2 s | ~3.0 s | ~1.7 s (×1.9) | **~0.3 s (×10)** |
**HMX (matmul fp16 tile 32×32) gagne énormément sur BigVGAN** parce que les conv1d
deviennent des im2col + matmul fp16, exactement ce que les tiles HMX font à 1 TFlops
crête par contexte. 4 contextes HMX V79 = ~5 TFlops crête, ~1.5 TFlops réel.
## 4. Leviers triés par ROI (gain × probabilité / effort)
### A. **Streaming pipeline** — gain TTFB, RTF inchangé
- Refactorer pour qu'audio commence à sortir dès que les premières frames du talker
sont disponibles, plutôt que d'attendre la fin de la phrase.
- Le decoder peut traiter par chunks (causal-ish), mais BigVGAN a un kernel k=7
dépendance gauche sur ~3 frames.
- Pipeline talkerCPdecoder en parallèle : temps total max des trois + overhead
de chunking **4-5 s au lieu de 7.9 s pour la même phrase, et TTFB ~500 ms**
au lieu de ~5 s.
- **Effort** : 1 session de refactor.
- **Risque** : faible. Bit-exact possible.
- **Gain RTF perçu** : massif (du « 5× plus lent que temps réel » au « le son
commence dans une demi-seconde et continue »).
### B. **CP : ctx ggml réutilisé entre sub-forwards** — réduit overhead
- Actuellement 15× ggml_init/free + 15× graph build par frame.
- Réutiliser un seul ggml_context + ggml_gallocr persistant graph build une fois,
re-compute 15 fois.
- Si overhead = 5 ms/sub-forward × 15 = 75 ms/frame, on espère gagner 50 ms/frame.
- **Effort** : session (~80 lignes de refactor dans cp_forward_cached_step).
- **Risque** : faible. Bit-exact attendu (mêmes ops, juste alloc différente).
- **Gain mesuré** : CP 110 ms ~60 ms espérés = **RTF total ~2.4** au lieu de 3.07.
### C. **Decoder : ctx ggml réutilisé + buffer mem 12GB→raisonnable**
- BigVGAN alloue 12 GB par appel. Probablement minor mais à vérifier.
- Réutilisation entre frames si on chunke (couplé à A).
- **Gain estimé** : 5-15% sur decoder, soit 0.2-0.5 s.
- **Effort** : ½ session.
### D. **BigVGAN sur backend Vulkan (Adreno)** — gain raisonnable
- Adreno 750 + ggml-vulkan : conv1d devient compute shader.
- Probable gain ×3-4 sur BigVGAN si bien câblé (mais ggml-vulkan + conv1d a des
trous).
- **Effort** : ~2 sessions (build vulkan, test, fix les ops manquantes).
- **Risque** : moyen (état ggml-vulkan + conv1d mal connu).
- **Gain** : BigVGAN 3.2 s 1 s = RTF total **~2.0**.
### E. **BigVGAN sur HMX V79** — le gros gain frontière
- Porter conv1d + convtranspose1d en kernels HMX via im2col + matmul fp16 tile.
- Existant : ggml-hexagon backend a déjà MUL_MAT HMX (utilisé pour Qwen3.5 prefill).
Manque : `supported_op` pour CONV_1D doit retourner true + le kernel im2col HVX.
- **Effort** : 1-2 semaines. Probablement bit-exact perdu (fp16 tile) mais audio
est tolérant.
- **Gain** : BigVGAN 3.2 s 0.3-0.5 s = RTF total **~1.0**.
- **Risque** : moyen-élevé (kernel HVX bit-exact n'est pas demandé pour l'audio,
mais artefacts possibles si quant mal calibrée).
### F. **CP sur HMX V79** — symétrique
- Le CP est un transformer dense 5L 1024-dim, exactement le pattern HMX.
- Port le forward CP sur HTP. ggml-hexagon supporte déjà les matmul HMX pour Qwen3.5.
- **Effort** : 1 session (le forward CP est court, ~200 lignes).
- **Gain** : CP 3.4 s 0.5-1 s = RTF total **~1.5**.
- **Risque** : faible (path déjà battu côté Qwen3.5).
- **Caveat** : les heads CP (15 × 2048 × 1024 = 30M params) restent CPU côté
dot-product. Coûtent ~30 M ops × 15 steps = 0.5 G ops/frame, ~5 ms/frame. OK.
### G. **B + F combinés** — chemin « ship-ready avec RTF<1.5 »
- CP overhead réduit (B) + CP sur HMX (F) CP 3.4 s ~0.3 s.
- Decoder reste CPU 3.2 s, talker 1 s.
- Total 4.5 s, RTF **1.7**. Pas <1 mais cap à dépasser.
### H. **B + E + F combinés** — RTF <1 ambitieux
- CP overhead + HMX + BigVGAN HMX CP 0.3 s + BigVGAN 0.4 s + talker 1 s + prefill 0.1 s = 1.8 s.
- **RTF 0.7**. Cible atteinte.
- **Effort total** : 3-4 semaines.
## 5. Recommandation
Plan ordonné par ROI décroissant :
1. **B. CP ctx réutilisé** session, RTF 3.07 2.4, bit-exact, faible risque).
2. **A. Streaming pipeline** (1 session, RTF perçu réduit drastiquement, bit-exact).
3. **F. CP forward sur HMX** (1 session, RTF ~1.5, faible risque, réutilise du
travail existant).
4. **E. BigVGAN HMX** (2-3 semaines, RTF ~0.7, risque qualité audio à valider).
Les étapes 1 et 2 sont des gains certains, courts, sans tester de nouveaux kernels.
Elles font la moitié du chemin. L'étape 3 est le prochain saut « propre », l'étape 4
est le sujet R&D qui fait franchir <1.
**Points de bascule** : si l'étape 1+2 donne RTF 2 avec streaming TTFB ~500 ms,
ça peut suffire pour shipper. RTF<1 n'est pas indispensable si l'audio commence
à jouer rapidement et continue plus vite qu'il ne se consomme **après** le démarrage
(= la queue se vide en cours de route).
## 6. Choses à vérifier / chantiers parallèles
- **HTP prefill talker** : n'a pas marché lors du test (hang 90s). Setup `ADSP_LIBRARY_PATH`
ou env supplémentaire requis. Pas urgent gain mineur (~0.05 s).
- **Quant heads/codec_embs CP** (Q8/Q4) : 240 MB de f32 dans s.heads+s.codec_embs.
Pourrait diminuer BW si les heads (CPU dot-product, pas ggml) deviennent bw-bound.
Mesuré insignifiant (5 ms/frame) pas un levier.
- **Decoder en f16** : à vérifier si poids déjà f16 ou f32. Si f32, bascule en f16
peut diviser BW par 2 si BW-bound. Mais BigVGAN est compute-bound (3.2 s
plafond NEON), donc gain attendu faible.
## Verdict
RTF<1 atteignable mais demande ~3-4 semaines de travail kernel hexagon (étape E ou F+E).
RTF 2 avec TTFB ~500 ms est atteignable en ~2 sessions de refactor (étapes B+A).
Cela suffit pour démo et probablement app, parce que ce qui compte côté UX c'est
le démarrage rapide + le rythme soutenu, pas la latence end-to-end pure.