# Déploiement ExecuTorch + QNN NPU — OnePlus Pad 3 *Procédure validée le 28 mars 2026* ## Prérequis - Tablette rootée (Magisk) - SELinux permissive (`su -c 'setenforce 0'`) - DSP permissions ouvertes (boot script `/data/adb/service.d/kazeia_dsp.sh`) - QNN SDK 2.42 installé sur le PC : `/opt/Kazeia/qnn_sdk_242/qairt/2.42.0.251225/` - ExecuTorch compilé pour Android ARM64 : `/opt/Kazeia/executorch/build-android/` ## Fichiers requis (TOUS obligatoires) | Fichier | Source | Rôle | |---------|--------|------| | `qnn_llama_runner` | `executorch/build-android/examples/qualcomm/oss_scripts/llama/` | Runner LLM | | `libqnn_executorch_backend.so` | `executorch/build-android/backends/qualcomm/` | Backend ExecuTorch ↔ QNN | | `libQnnHtp.so` | `qnn_sdk_242/.../lib/aarch64-android/` | QNN HTP runtime | | `libQnnHtpV79Stub.so` | `qnn_sdk_242/.../lib/aarch64-android/` | Stub CPU → DSP | | `libQnnHtpV79Skel.so` | `qnn_sdk_242/.../lib/hexagon-v79/unsigned/` | Skel DSP (hexagon) | | `libQnnHtpPrepare.so` | `qnn_sdk_242/.../lib/aarch64-android/` | Préparation graphe HTP | | `libQnnSystem.so` | `qnn_sdk_242/.../lib/aarch64-android/` | Système QNN | | `libQnnModelDlc.so` | `qnn_sdk_242/.../lib/aarch64-android/` | Chargement modèles DLC | | `libQnnHtpNetRunExtensions.so` | `qnn_sdk_242/.../lib/aarch64-android/` | Extensions réseau HTP | | `hybrid_llama_qnn.pte` | `models_qnn/qwen3-*-executorch/` | Modèle ExecuTorch | | `tokenizer.json` | `models_qnn/qwen3-*-executorch/` | Tokenizer HuggingFace | **IMPORTANT** : Les libs Skel DOIVENT venir du même SDK que celui utilisé pour compiler le .pte. Ne PAS utiliser les libs vendor de la tablette (`/vendor/lib64/hw/audio/`). ## Script de déploiement ```bash #!/bin/bash QNN="/opt/Kazeia/qnn_sdk_242/qairt/2.42.0.251225" ET="/opt/Kazeia/executorch" DIR="/data/local/tmp/kazeia-et" MODEL_DIR="/opt/Kazeia/models_qnn/qwen3-0_6b-executorch" # ou qwen3-1_7b-executorch # Créer répertoire adb shell "rm -rf $DIR && mkdir -p $DIR/outputs" # Libs QNN SDK 2.42 adb push $QNN/lib/aarch64-android/libQnnHtp.so $DIR/ adb push $QNN/lib/aarch64-android/libQnnHtpV79Stub.so $DIR/ adb push $QNN/lib/hexagon-v79/unsigned/libQnnHtpV79Skel.so $DIR/ adb push $QNN/lib/aarch64-android/libQnnHtpPrepare.so $DIR/ adb push $QNN/lib/aarch64-android/libQnnSystem.so $DIR/ adb push $QNN/lib/aarch64-android/libQnnModelDlc.so $DIR/ adb push $QNN/lib/aarch64-android/libQnnHtpNetRunExtensions.so $DIR/ # ExecuTorch adb push $ET/build-android/backends/qualcomm/libqnn_executorch_backend.so $DIR/ adb push $ET/build-android/examples/qualcomm/oss_scripts/llama/qnn_llama_runner $DIR/ # Modèle + Tokenizer adb push $MODEL_DIR/hybrid_llama_qnn.pte $DIR/ adb push $MODEL_DIR/tokenizer.json $DIR/ # Permissions adb shell "chmod +x $DIR/qnn_llama_runner" ``` ## Exécution ```bash adb shell "su -c 'cd /data/local/tmp/kazeia-et && \ export LD_LIBRARY_PATH=/data/local/tmp/kazeia-et && \ export ADSP_LIBRARY_PATH=/data/local/tmp/kazeia-et && \ ./qnn_llama_runner \ --model_path hybrid_llama_qnn.pte \ --tokenizer_path tokenizer.json \ --decoder_model_version qwen3 \ --output_path outputs/outputs.txt \ --performance_output_path outputs/perf.txt \ --shared_buffer \ --prompt \"Votre prompt ici\" \ --system_prompt \"Tu es Kazeia, compagnon écoute émotionnelle.\" \ --temperature 0.7 \ --seq_len 256 \ --eval_mode 1'" ``` ### Paramètres clés | Paramètre | Valeur | Description | |-----------|--------|-------------| | `--decoder_model_version` | `qwen3` | Template de chat Qwen3 | | `--eval_mode` | `1` | Mode hybride (prefill + KV cache) | | `--shared_buffer` | (flag) | **OBLIGATOIRE** — mémoire partagée CPU↔DSP | | `--output_path` | chemin | Fichier de sortie texte | | `--temperature` | 0.0-1.0 | 0 = déterministe, 0.7 = créatif | | `--seq_len` | 128-4096 | Longueur max (prompt + réponse) | ## Performances mesurées ### Qwen3-0.6B (660 MB) - **Chargement** : 0.86s - **Prefill** : 31ms (451 tok/s) - **Génération** : 93.15 tok/s - **Temps au premier token** : 31ms - **RAM** : ~698 MB ### Qwen3-1.7B (1.7 GB) - **Génération** : ~25.7 tok/s (benchmark mars 2026) ### Mistral-Nemo 12B (7.4 GB) - **Génération** : ~5.1 tok/s (benchmark mars 2026) ## Modèles disponibles | Modèle | Fichier .pte | Taille | Qualité FR | |--------|-------------|--------|------------| | Qwen3-0.6B | `qwen3-0_6b-executorch/hybrid_llama_qnn.pte` | 660 MB | Basique | | Qwen3-1.7B | `qwen3-1_7b-executorch/hybrid_llama_qnn.pte` | 1.7 GB | Bonne | | Mistral-Nemo 12B | `mistral-nemo-executorch/hybrid_llama_qnn.pte` | 7.4 GB | Excellente | ## Erreurs courantes ### `loadRemoteSymbols failed with err 4000` - **Cause** : Libs Skel manquantes ou incompatibles - **Fix** : Vérifier que `libQnnHtpV79Skel.so` vient du SDK 2.42 (hexagon-v79/unsigned/), pas du vendor ### `SoC model (SnapdragonModel) is unknown` - **Cause** : Utilisation des libs vendor au lieu des libs SDK - **Fix** : Ne PAS utiliser `/vendor/lib64/hw/audio/libQnn*.so` ### Runner se termine sans générer de texte - **Cause** : `--shared_buffer` et `--output_path` manquants - **Fix** : Ajouter les deux paramètres ### `Failed to create transport for device` - **Cause** : `libQnnModelDlc.so` manquant - **Fix** : Copier depuis `qnn_sdk_242/.../lib/aarch64-android/` --- *Projet Kazeia — Damien Micottis & Richard Loyer*