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