Kazeia-engine/dist/PERF_VULKAN_TESTS.md

172 lines
6.6 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.

# Tests Vulkan Adreno 830 — verdict + raisons (29/05)
Session de re-analyse globale plateforme. Le HTP avait été exclu. Le GPU
n'avait JAMAIS été testé. Suite à l'autorisation de Richard, exploration
ggml-vulkan sur Adreno 830 du SM8750.
## Préparatifs build
Chaîne build non-triviale, dépendances manquantes dans NDK r27d :
1. `SPIRV-Headers` : téléchargé depuis github (vulkan-sdk-1.3.275.0), installé
dans `/tmp/spirv-install` et dans le sysroot NDK.
2. `vulkan.hpp` (header C++) : téléchargé depuis Vulkan-Hpp v1.3.275,
copié dans le sysroot NDK Vulkan include.
3. `libomp.so` : copié depuis NDK clang/18/lib/linux/aarch64 vers tablette
(ggml-vulkan linked OpenMP).
Build cmake : `dist/build_ggml_vulkan.sh` produit `libggml-vulkan.so` (41 MB
avec shaders SPIR-V embarqués). Détection device :
```
ggml_vulkan: Found 1 Vulkan devices:
ggml_vulkan: 0 = Adreno (TM) 830 (Qualcomm Technologies Inc. Adreno Vulkan Driver)
uma: 1 | fp16: 1 | bf16: 0 | warp size: 64
shared memory: 32768 | int dot: 0 | matrix cores: none
```
Caractéristiques clés :
- **UMA=1** (Unified Memory Architecture) : même DRAM que CPU
- **fp16=1** OK ; bf16=0 ; **matrix cores: none** (juste compute shaders)
- Warp size 64
- L2/shared 32 KB
## Test 1 : decoder Vulkan buffer + CPU compute (sans sched)
KZTTS_DECODER_VULKAN=1 sans KZTTS_DECODER_SCHED=1 :
| Phrase | Total | RTF | WAV md5 |
|---|---:|---:|---|
| Bonjour Kazeia (baseline CPU) | 6.75 s | 2.45 | c3dd71... |
| Bonjour Kazeia + Vulkan buf | 6.80 s | 2.47 | c3dd71... ✓ |
| Bonsoir 41 frames + Vulkan buf | 8.34 s | 2.44 | — |
- Poids decoder sur Vulkan-allocated buffer (host-visible UMA)
- Compute reste CPU pur via `ggml_graph_compute_with_ctx`
- **WAV bit-exact identique au baseline**
- Perf : **neutre, marge bruit**
UMA = pas de gain BW exclusif (CPU et GPU partagent la DRAM). Poids alloués
par Vulkan driver ont peut-être un meilleur alignment/prefetch que CPU
malloc mais l'effet est non-mesurable (±2 ms par frame, bruit).
## Test 2 : decoder Vulkan + sched (GPU compute attendu)
KZTTS_DECODER_VULKAN=1 KZTTS_DECODER_SCHED=1 :
Sched créé avec `[Vulkan, CPU]`. Mais GGML_SCHED_DEBUG=2 montre :
```
node # 1 (CONT): bigvgan_input [CPU]
node # 2 (PAD): [CPU]
node # 3 (IM2COL): [CPU] -- decoder.initial.weight (21M) [CPU]
...
Stats : 14394 [CPU], 0 [Vulkan0]
```
**Tous les nœuds sur CPU**. Pourquoi ?
- Adreno UMA buffer is_host=true (Vulkan considère que c'est de la mémoire
CPU-accessible).
- Le sched logic (`ggml-backend.cpp:916-928`) : si weight is_host AND
src_backend_id == last (CPU) → offload candidat. Sinon → reste sur weight
backend.
- Avec UMA, `backend_from_buffer(vulkan_buf)` retourne probablement l'index
CPU (logique inversée pour host-visible buffers).
- Donc tous les ops tombent sur CPU.
Pour forcer compute Vulkan, faudrait soit :
- Refactor `stage_bigvgan_sched` pour appeler `ggml_backend_graph_compute(vk, gf)`
directement (mais alors ops non-supportées crashent au lieu de fallback CPU)
- Implémenter sched custom forcé
Hors scope pour ce test exploratoire.
## Test 3 : talker offload via llama.cpp (n_gpu_layers=99 → Vulkan)
KZTTS_TALKER_VULKAN=1 :
```
load_tensors: offloaded 29/29 layers to GPU
talker: Vulkan0 (GPU offload)
```
**29/29 couches sur GPU** confirmé. Mais perf :
| Étape | Baseline CPU | Talker Vulkan | Delta |
|---|---:|---:|---:|
| Prefill | 87 ms | **775 ms** | **+790%** |
| Talker/frame | 32 ms | 45 ms | +44% |
| CP/frame | 70 ms | 81 ms | +15% (thermal) |
| Decoder | 3.3 s | 6.6 s | +100% (thermal/GPU bus contention) |
| **Total** | **6.75 s** | **15.4 s** | **+128%** |
WAV md5 différent (a8f03d66 vs c3dd71cb) : GPU fp16 diverge légèrement
de CPU NEON f32.
Raisons de la régression massive :
1. **Talker = Qwen3-TTS 0.6B** : trop petit pour amortir overhead pipeline
GPU (kernel launch, sync, copy embedding/logits CPU↔GPU à chaque token).
2. **Decode batch=1** : pattern défavorable pour GPU compute shaders
(warps largement vides).
3. **Prefill 775 ms** : probablement compile shaders à chaud + premier
chargement des poids sur Adreno.
4. **GPU bus contention** : Adreno + CPU sur la même DRAM 25 GB/s effective ;
les jobs GPU saturent la BW ce qui ralentit aussi le CP et decoder CPU
qui tournent en parallèle.
## Verdict Vulkan
| Pattern | Gain net | Pourquoi |
|---|---|---|
| Vulkan buf + CPU compute | **0% (neutre)** | UMA = pas de BW gain exclusif |
| Vulkan compute via sched | impossible | sched route UMA → CPU automatiquement |
| Talker Vulkan offload | **-128% (régression massive)** | 0.6B + batch=1 + bus contention |
| BigVGAN Vulkan compute direct | **non testé** (faut refactor sched) | exigerait crash si ops non-supportées |
**Conclusion** : comme le HTP, Vulkan Adreno 830 sur SM8750 **n'apporte
pas de gain net** sur ce workload TTS. Trois facteurs :
1. **UMA** : pas de mémoire exclusive GPU, donc pas de gain BW
2. **Pas de matrix cores** : Adreno 830 fait du compute shader standard
(vs NEON 4-wide CPU)
3. **Workload TTS** : autorégressif batch=1, mauvais pour GPU paradigm
Cette confirmation valide définitivement l'orientation **NEON CPU optimisé**
comme la voie unique réaliste pour ce hardware sur ce TTS.
## Consommation énergétique (préoccupation Richard)
Non mesurée précisément, mais qualitativement :
- Vulkan talker offload : pipeline ×2.3 plus long. Probable consommation
ENERGIE × 2 (CPU encore actif pendant l'attente GPU).
- Vulkan buffer + CPU : conso similaire au baseline.
- NEON CPU optim : meilleure efficience énergétique (compute compact,
pas de driver overhead, pas de bus traffic supplémentaire).
**Reste sur NEON CPU pour la prod, conso optimale.**
## Path résiduels non explorés (par honnêteté)
- **BigVGAN compute forcé sur Vulkan via API directe** : skipper le sched,
appeler `ggml_backend_graph_compute(vk, gf)` directement. Ops non-supportées
crashent. Refactor lourd pour wrapping fallback.
- **Vulkan shader custom** pour conv1d optimisé pour Adreno spécifique :
chantier sérieux, semaines.
- **Mix CPU/GPU avec gros chunks** : faire petits ops sur CPU, gros matmul
isolés sur GPU. Demande analyse fine + refactor.
Aucun ne paraît livrable dans une session vu le pattern de régression
observé.
## Code committé
- `dist/build_ggml_vulkan.sh` : script build ggml-vulkan pour Android arm64
- `dist/jni/tts_engine.cpp` : nouveau path optionnel
- `KZTTS_VULKAN_LIB=/path/libggml-vulkan.so` : charge backend Vulkan
- `KZTTS_DECODER_VULKAN=1` : decoder utilise buft Vulkan (neutre)
- `KZTTS_TALKER_VULKAN=1` : talker offload (régresse, à éviter)
Tous opt-in, désactivés par défaut. Path principal (NEON CPU) intact.