# 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.