Outils de profilage et d'aide à la décision pour modèles LLM exécutés sur Android avec plusieurs backends de calcul. Le projet analyse les modèles par couche et, pour les architectures MoE, suit les opérations liées aux experts. Il compare également les placements CPU, GPU et HTP ainsi que leurs ratios.
Le projet couvre quatre niveaux distincts :
- inspection statique d'un modèle GGUF ou Safetensors ;
- estimation de mémoire, compatibilité et coût ;
- mesure réelle sur l'appareil, par opération et par couche ;
- validation de la qualité et du placement avant acceptation.
Les estimations ne sont jamais présentées comme des mesures. Un modèle n'est déclaré validé qu'après contrôle de qualité et preuve d'exécution sur le backend attendu.
- lecture de GGUF et Safetensors locaux ou distants ;
- support des dépôts Hugging Face par requêtes HTTP partielles ;
- détection de l'architecture, des couches, du nombre d'experts et du top-k ;
- inventaire des tenseurs, formats et tailles de blocs ;
- estimation de RAM, trafic mémoire et coûts par couche ;
- analyse de quantification à partir d'échantillons de poids réels ;
- génération d'un plan de types par tenseur ;
- conversion contrôlée d'un GGUF avec conservation des types non modifiés ;
- comparaison de perplexité entre modèle de référence et candidat ;
- profil HTP, GPU et CPU par opération et par couche ;
- mesure des copies entre backends ;
- télémétrie Android : RAM, mémoire de processus, charge GPU et températures ;
- fusion de campagnes de placements et de ratios ;
- journalisation des prédictions et des résultats mesurés.
Les calibrations fournies avec le projet proviennent principalement d'un appareil SM8850 avec HTP v81. Elles servent de point de départ et non de garantie pour un autre appareil.
La calibration dense ne contient encore que peu de points. La calibration MoE historique est désactivée par défaut lorsqu'elle ne correspond pas au runtime mesuré. Les prédictions hors de la zone calibrée sont marquées comme peu fiables.
- Python 3.10 ou plus récent ;
- NumPy ;
- ADB pour les mesures Android ;
- un build compatible de llama.cpp pour la conversion, la perplexité et les essais d'inférence ;
- les sorties de profilage correspondantes pour les analyses HTP, GPU, CPU et copies inter-backends.
py -m pip install -r requirements.txt
py -m unittest discover -s tests -vÉtat au 15 septembre 2026 : 61 tests passent.
Le catalogue central quant_formats.py sépare désormais :
- le dtype ou type GGUF stocké ;
- la recette de quantification (PTQ/QAT, groupe, échelles, codebook) ;
- le layout de chargement ou de repack du backend ;
- le dtype des activations et de l'accumulation.
Il couvre les types GGUF du fork, dont MXFP4, NVFP4, Q1_0, Q2_0, IQ et TQ,
ainsi que les dtypes Safetensors entiers, flottants, FP8, FP6 et FP4. Les
octets Safetensors viennent des data_offsets et sont contrôlés contre le
dtype et la forme ; un dtype inconnu est refusé au lieu d'être supposé BF16.
Pour HTP, le rapport distingue native_tiled, converted_lossy, converted
et unknown. unknown signifie que le chemin n'est pas démontré dans le fork,
pas que le format est impossible sur toute NPU Qualcomm.
Le nombre d'opérations logiques n'est jamais traité comme un nombre de RPC.
Sans trace OPBATCH, le niveau L3 est présenté comme un plafond
kernel+mémoire. Avec une trace, le résidu temps batch - somme des kernels est
conservé séparément ; il peut contenir dispatch, synchronisation, queue, DMA
non instrumenté et biais de mesure.
GGUF / Safetensors / Hugging Face
│
▼
Lecture et inspection des tenseurs
│
┌───────┴────────┐
▼ ▼
Analyse statique Échantillons de poids
RAM / couches erreur de quantification
│ │
└───────┬────────┘
▼
Plan de quantification
│
conversion contrôlée
│
┌───────┴────────┐
▼ ▼
Gate qualité Test sur appareil
PPL / cohérence HTP / GPU / CPU
│ │
└───────┬────────┘
▼
Rapport couche / expert / ratio
py profiler.py D:\models\model.gguf --backend HTP --budget-gb 9
py profiler.py D:\models\model.gguf `
--backend HTP `
--budget-gb 9 `
--safety-margin-gb 1.5 `
--layer-quant-impactpy profiler.py D:\models\transformers_modelLe lecteur unifié accepte un dossier avec config.json, un fichier
.safetensors, un index .safetensors.index.json ou plusieurs shards.
py predict_from_hf.py auteur/modele-GGUF
py predict_from_hf.py auteur/modele-GGUF `
--file modele.Q4_0.gguf `
--budget-gb 7.5Pour une expérience reproductible, utiliser une révision fixe avec les outils
qui proposent --revision.
py predict_quantization.py `
--local D:\models\model.safetensors `
--max-tensors 32 `
--sample-blocks 64 `
--json
py predict_quantization.py `
--repo auteur/modele `
--file model.safetensors.index.json `
--revision COMMIT `
--max-tensors 32 `
--sample-blocks 64Le résultat mesure une erreur locale sur les poids échantillonnés. Il ne remplace pas la mesure des logits, de la perplexité ou du débit réel.
Analyse seule :
py fix_htp_model.py D:\models\source.ggufCréation d'un candidat :
py fix_htp_model.py D:\models\source.gguf `
--apply `
--llama-quantize D:\llama\llama-quantize.exe `
--out D:\models\candidate.ggufLe fichier de types est exhaustif : chaque tenseur conserve son type d'origine
sauf lorsqu'une modification est explicitement recommandée. Le mode COPY
n'est pas utilisé comme type de sortie de conversion.
py quality_gate.py `
--perplexity D:\llama\llama-perplexity.exe `
--reference D:\models\source.gguf `
--candidate D:\models\candidate.gguf `
--corpus D:\corpus\wiki.test.raw `
--chunks 30 `
--output D:\results\quality_gate.jsonLe gate refuse un résultat manquant, un processus en erreur ou une régression supérieure aux seuils configurés.
py full_quant_pipeline.py `
--source D:\models\source.gguf `
--candidate D:\models\candidate.gguf `
--tool-dir D:\llama\build\bin `
--corpus D:\corpus\wiki.test.raw `
--work-dir D:\results\candidate `
--resumeÉtapes : analyse des poids, génération du plan, conversion, contrôle PPL et, si les paramètres ADB sont fournis, vérification sur l'appareil. Sans preuve device, l'état final reste volontairement incomplet.
Une capture compatible doit contenir les lignes de profil par opération. Une fois le log récupéré :
py parse_hexagon_profile.py device_logs\server.log `
--out device_logs\htp_trace.jsonl
py profiler.py D:\models\model.gguf `
--backend HTP `
--live-trace device_logs\htp_trace.jsonl `
--measured-tps 20.5 `
--emit-tensor-type-file D:\results\tensor_types.txtLa trace fournit les temps par opération, les couches, les chemins de calcul, la pression mémoire locale lorsqu'elle est disponible et le coût des opérations MoE.
py parse_opencl_profile.py device_logs\cl_profiling.csv `
--out device_logs\gpu_report.md
py parse_opencl_profile.py device_logs\cpu_profiling.csv `
--out device_logs\cpu_report.mdLe même parseur reconnaît le type de trace grâce aux colonnes disponibles. Les métriques absentes de la capture restent indiquées comme indisponibles.
py parse_backend_copy_profile.py `
device_logs\backend_copy_profile.csv `
--out device_logs\copy_report.mdLe rapport présente les paires source/destination, le nombre de copies, les octets cumulés, le temps et les couches frontières identifiables. Un run sur un seul backend peut légitimement ne contenir aucune copie.
Le projet agrège les tenseurs et opérations par numéro de couche. Il compare le coût statique estimé au coût réellement observé et identifie les couches chaudes.
Pour un MoE, les opérations ARGSORT, MUL_MAT_ID et MUL_MAT_ID_NX sont
agrégées par couche. Dans les GGUF courants, les experts d'une couche peuvent
être regroupés dans un tenseur 3D ayant un type uniforme. Dans ce cas, le grain
déployable est la couche d'experts, pas l'expert individuel.
Une campagne doit tester l'ordre des backends autant que leur proportion. Les noms de dossiers reconnus sont :
htp_onlyetgpu_only;htp_gpu_8020,htp_gpu_5050,htp_gpu_2080;gpu_htp_8020,gpu_htp_5050,gpu_htp_2080.
campagne/
├── htp_only/
│ ├── server.log
│ └── resp.json
├── gpu_htp_8020/
│ ├── server.log
│ ├── resp.json
│ ├── cl_profiling.csv
│ ├── cpu_profiling.csv
│ └── backend_copy_profile.csv
└── ...
py analyze_ratio_campaign.py D:\campaigns\moe `
--json D:\campaigns\moe\summary.json `
--out D:\campaigns\moe\summary.mdLe rapport sépare :
- ratio demandé ;
- présence des couches par backend ;
- proportion du nombre d'opérations ;
- proportion du temps de calcul ;
- coût MoE par couche ;
- temps et volume des copies ;
- instrumentation manquante ;
- cohérence entre backends demandés et observés.
Le meilleur débit reste un candidat jusqu'aux contrôles de qualité, stabilité, mémoire et température.
py android_telemetry.py `
--proc-name llama `
--interval 0.2 `
--duration 30 `
--out device_logs\android_telemetry.jsonl
py telemetry_dashboard.py `
--name llama `
--interval 0.5 `
--log device_logs\telemetry_session.jsonlL'application compagnon se trouve dans android/hw_monitor/. Elle expose un
état local consommé par le tableau de bord via un tunnel ADB.
launch_llm.py génère les métadonnées nécessaires au chargement d'un GGUF sans
lancer lui-même l'inférence :
py launch_llm.py D:\models\model.gguf --ctx 4096 --jsonLa sortie indique le contexte utilisable, la présence d'un modèle MoE, les familles de tenseurs et les contrôles restant à effectuer.
| Fichier | Rôle |
|---|---|
profile_model.py |
Lecture, tailles exactes, agrégation par famille/couche, simulation et analyse des traces. |
profiler.py |
Interface principale pour budget, backend, couches, traces et rapports. |
tensor_sources.py |
GGUF/Safetensors local ou distant, y compris multi-shards. |
weight_sampler.py |
Échantillonnage déterministe et borné des poids. |
predict_quantization.py |
Comparaison des formats à partir des poids. |
| Fichier | Rôle |
|---|---|
predict_from_hf.py |
Inspection rapide d'un GGUF distant par son en-tête. |
predict_from_hf_v4.py |
Contrôles de formats et risques par familles d'opérations. |
predict_from_hf_v5.py |
Plan par couche/famille et analyses de qualité progressives. |
predictor.py |
Prédiction tenant compte de l'état de l'appareil. |
capability_db.py |
Capacités connues, contraintes et résultats de validation. |
adaptive_lever.py |
Recommandation du prochain levier à mesurer. |
| Fichier | Rôle |
|---|---|
fix_htp_model.py |
Construction et application contrôlée d'un plan de types. |
quality_gate.py |
Comparaison de perplexité et verdict bloquant. |
full_quant_pipeline.py |
Pipeline reprenable de la source à la preuve device. |
launch_llm.py |
Métadonnées de préparation pour une application Android. |
| Fichier | Rôle |
|---|---|
parse_hexagon_profile.py |
Profil HTP par opération et couche. |
parse_opencl_profile.py |
Profils GPU et CPU. |
parse_backend_copy_profile.py |
Copies et frontières entre backends. |
analyze_ratio_campaign.py |
Fusion multi-run couche/expert/ratio. |
live_monitor.py |
Lecture d'une trace en cours d'écriture. |
android_telemetry.py |
Télémétrie Android indépendante du rapport d'inférence. |
telemetry_dashboard.py |
Affichage temps réel sur le PC. |
device_logs/: traces et rapports locaux ;pipeline_runs/: états et sorties des pipelines ;predictor_v1_outcomes.jsonl: prédictions comparées aux mesures ;- rapports
.md: résultats lisibles ; - fichiers
.jsonet.jsonl: données structurées pour automatisation.
Les gros logs doivent rester hors du dépôt principal. Un rapport doit conserver le hash du modèle, les versions des exécutables, les options de lancement, le prompt, la seed, le contexte, la température et les fichiers bruts associés.
| État | Signification |
|---|---|
predicted |
Déduit du modèle et de calibrations antérieures. |
simulated |
Produit par un modèle de coût. |
measured |
Observé pendant un run. |
validated |
Mesuré, reproductible et passé par les gates requis. |
Une analyse d'en-tête distante ne peut pas produire un état validated.
- prévoir une marge RAM pour le KV cache, les activations et les buffers ;
- séparer les runs froids et chauds ;
- enregistrer les températures de début et de fin ;
- utiliser les mêmes prompts, seeds et longueurs pour les comparaisons ;
- préférer des séquences A/B/A/B aux comparaisons uniques ;
- vérifier le backend réellement engagé ;
- refuser une conversion si la qualité ou la preuve d'exécution manque ;
- ne pas extrapoler silencieusement vers un runtime différent.
- pas de quantification distincte de chaque expert lorsque le format les regroupe dans un tenseur unique ;
- identité de l'expert actif absente de certaines traces ;
- copies spécialisées des experts pas encore entièrement observables ;
- fusion temporelle HTP/GPU/CPU limitée par les horodatages disponibles ;
- calibration MoE à reconstruire pour les runtimes récents ;
- contrôle qualité pas encore intégré à toutes les campagnes de ratios ;
- les poids seuls ne prédisent pas la contention, les splits ou le meilleur placement réel.
Après compactage des événements :
- trace de 80,3 Mo : environ 1,98 seconde ;
- trace de 467,3 Mo : environ 12,19 secondes.
Python reste adapté au pipeline actuel. Les améliorations de bas niveau doivent d'abord viser les informations manquantes dans les traces et la consommation mémoire.
py -m unittest discover -s tests -vLa suite couvre les blocs GGUF, codecs, sources locales/distantes, Safetensors multi-shards, requêtes HTTP partielles, gates qualité, preuve device, parseur HTP, copies inter-backends, distinction ratio demandé/observé et coût MoE par couche.
Avant une modification :
- ajouter une fixture minimale reproduisant le cas ;
- conserver les états de preuve séparés ;
- ne pas remplacer une mesure absente par zéro ;
- associer toute calibration à l'appareil et au runtime concernés ;
- lancer la suite complète de tests.
Les contributions les plus utiles sont les traces d'autres appareils, les campagnes MoE avec plusieurs top-k, les mesures des copies d'experts et les tests de placement effectif.