Kazeia-engine/dist/PATCH_QNN_PROD.md

104 lines
4.5 KiB
Markdown
Raw 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.

# Patch QNN options pour la prod gelée — sans migration
**Action 1 isolée** de la décision migration `libkazeia_stt`. Ce patch ne touche pas le chemin code, juste les options ORT passées à QNN. Réversible en 4 lignes.
## Constat
Dans `WhisperHybridEngine.kt:101-102` actuel :
```kotlin
val encOpts = OrtSession.SessionOptions()
encOpts.addQnn(mapOf("backend_path" to htpPath))
```
Ces options QNN par défaut donnent :
- `htp_performance_mode = balanced` (scheduler équilibré, pas freq max NPU)
- pas de `htp_arch` explicite → ORT auto-détecte, fallback générique sur certains paths
- pas de `enable_htp_fp16_precision` → conversion implicite parfois sub-optimale
- pas de `SetGraphOptimizationLevel(ALL_OPT)` activé explicitement
**Coût mesuré sur SD8 Elite (SM8750, Whisper-Small)** : encoder 309 ms au lieu de 62 ms, decoder 48 ms/tok au lieu de 14 ms/tok.
## Patch (4 lignes Kotlin)
Dans `WhisperHybridEngine.kt`, remplacer les blocs encOpts/decOpts :
```kotlin
val encOpts = OrtSession.SessionOptions()
encOpts.addQnn(mapOf(
"backend_path" to htpPath,
"htp_performance_mode" to "burst",
"htp_arch" to "79",
"enable_htp_fp16_precision" to "1",
))
encOpts.setOptimizationLevel(OrtSession.SessionOptions.OptLevel.ALL_OPT)
```
Idem pour `decOpts`. Et symmetric pour le fallback `encoder_npu` path si présent (ligne 134 environ).
## Gain mesuré attendu
Sur le port C++ avec exactement ces options, mesures stables 5 runs warm (audio FR continu, 12 tokens / 1.6 s) :
| Étape | Sans options | Avec burst+v79+fp16 |
|---|---:|---:|
| Encoder | 309 ms | 62 ms |
| Decoder/token | 48 ms | 14 ms |
| Total (12 tok) | 962 ms | 320 ms |
Hypothèse cohérente : sur la prod Kotlin actuelle (820 ms / 22 tok), avec patch :
- Encoder 125 → ~62 ms
- Decoder 22 × 23 → 22 × ~14 ms = 308 ms
- Total ≈ **560 ms (-32 %)**
**À valider sur audio Pierre réel.** L'extrapolation est mathématique, pas un bench prod direct.
## Script de bench audio Pierre
Pour valider le gain sur les audios prod typiques (phrases courtes thérapeutiques) :
```kotlin
// Dans un mode debug de l'app, ou un test Android instrumenté :
val engine = WhisperHybridEngine(nativeLibDir)
engine.load(modelPath = WHISPER_DIR)
// Charge audio Pierre déjà capturé en prod (ShortArray PCM16 16kHz mono)
val samples: List<Pair<String, ShortArray>> = loadPierrAudioSamples() // 10-20 samples
samples.forEach { (name, pcm) ->
val t0 = System.currentTimeMillis()
val r = engine.transcribe(pcm, "fr")
val dt = System.currentTimeMillis() - t0
Log.i("BENCH", "$name : ${pcm.size / 16000.0}s audio, $dt ms total, text='${r.text}'")
}
```
Compare le `dt` total avec et sans patch sur **les mêmes audios** :
- Si gain ≥ 25 % en moyenne : patch validé, à merger en l'état
- Si gain < 25 % : enquêter (probable que l'app n'utilise pas les bonnes libs QNN, vérifier `jniLibs/arm64-v8a/libQnnHtp*.so` et version `qnn-runtime`)
## Pourquoi c'est sans risque
1. **Code path identique** : c'est de la config ORT, pas un changement de modèle ni de pipeline.
2. **Réversible** : 4 lignes à retirer si jamais ça régresse.
3. **Compatibilité** : `htp_arch=79` est explicite mais valide sur SD8 Elite. Sur autre device (Gen3 = V75), il faudrait conditionner sur `Build.SOC_MANUFACTURER`/`SOC_MODEL`. Pour Kazeia mono-tablette (Pad3 = SM8750 = V79), c'est sûr.
4. **Pas de dégel STT** : le contexte QAIRT est inchangé, le modèle est inchangé, la classe Kotlin est inchangée hors les 4 lignes.
## Après ce patch, la décision migration `libkazeia_stt`
Le delta restant entre prod patchée et lib unifiée :
| | Prod + patch QNN | Lib unifiée |
|---|---:|---:|
| Total (22 tok / 1.6 s) | ~560 ms | ~450 ms |
| Delta | | **-110 ms** |
| RAM | 545 MB | **377 MB (-168 MB)** |
| Code | îlot STT (527 l Kotlin + 203 l C++ mel + 197 l VAD) | unifié dans `libkazeia_stt.so` + `SttEngine.kt` |
Décision migration **conditionnée à** :
1. Patch QNN posé et mesuré sur audio Pierre
2. A/B accuracy CER/WER lib unifiée vs prod patchée sur les mêmes audios **à mesurer in-app**
3. Si #2 passe (CER +5 % relatif vs prod), la migration vaut le coup pour les -168 MB et la cohérence code
4. Si #2 dégrade, on garde la prod patchée et la lib unifiée reste un POC
L'A/B accuracy reste le seul point dur non mesurable en standalone. Le dev offre un patch derrière l'interface existante (`WhisperHybridEngine` `SttEngine` swap derrière `SttStage`) c'est la bonne façon de mesurer sans s'engager.