Optim decoder identifiée après bench sur audio FR continu (39 tokens) :
le `std::vector<>::emplace_back(data)` à chaque step recopiait 12 MB de
self KV inutilement = 2.4 GB de bande passante mémoire sur le loop entier.
Refactor zero-copy :
- Pré-allocation UNE FOIS des buffers persistants (self_k/v, mask, input_ids,
position_ids) au début de transcribe()
- Pré-création UNE FOIS des Ort::Value pointant sur ces buffers (les Ort::Value
sont des "borrowing views" — ORT lit les buffers au Run())
- Cache des indices k_self_out / v_self_out / logits_out (parsing 1 fois,
pas N fois dans le loop)
- Update self KV : memcpy direct depuis dec_outputs vers nos buffers persistants
(qui sont déjà les inputs du step suivant)
- mask_buf updaté à 1 fp16 par step (juste le slot R-to-L), pas 200
Bench zero-copy sur Snapdragon 8 Elite :
- Decoder/token : 98 ms -> 36 ms (-63%)
- Decoder total 39 tokens : 3831 ms -> 1390 ms (-64%)
- RTF audio 10s FR continu : 0.42 -> 0.18 (mieux que prod 0.51)
- RAM peak : 378 MB inchangée (zero-copy n'augmente pas la peak)
Sanity vérifié au runtime :
- Logits decoder shape = 51865 elements (= constante VOCAB_SIZE OK,
pas de buffer over-read sur argmax). Le vocab.json contient 50258 tokens
texte, les 1607 manquants sont les tokens langue/timestamp non décodés.
Doc STT_INTEGRATION.md :
- Chiffres perf à jour (RTF 0.18, decoder 36 ms/token)
- Nouvelle section "Checklist d'intégration" (6 points à vérifier en premier
côté app : System.loadLibrary, transcribe vs WhisperHybridEngine, null/edge,
VAD drop-in, threading IO, cycle de vie release)
- Roadmap optim future via Ort::IoBinding (gain estimé 30% sur decoder restant)