Kazeia-engine/dist/PERF_VULKAN_TESTS.md

6.6 KiB
Raw Blame History

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.