perf-host-optimization

Par nvidia · skills

Profile et optimise la surcharge hôte/CPU de TensorRT-LLM à l'aide de line_profiler (avec prise en charge de nsys prévue). Exécute des cycles itératifs de profilage-analyse-optimisation-validation. À utiliser lorsque l'utilisation du GPU est faible ou pour optimiser le débit de PyExecutor.

npx skills add https://github.com/nvidia/skills --skill perf-host-optimization

Skill d'Optimisation des Performances Hôte

Automatise la détection et l'optimisation des surcharges côté hôte (CPU) dans le backend PyTorch de TensorRT-LLM.

Quand l'utiliser

  • L'utilisation GPU est faible pendant l'inférence (bottleneck CPU suspecté)
  • L'utilisateur demande de réduire la surcharge hôte ou la latence CPU
  • Optimisation du débit de PyExecutor (requêtes/sec)
  • Besoin de profilage ligne par ligne de fonctions Python spécifiques

Détecter un Bottleneck CPU

line_profiler mesure le temps CPU est dépensé mais pas si le CPU est le bottleneck. Quand l'utilisateur demande une confirmation, ou qu'il n'y a pas de conditions de fin claires pour les optimisations, utilisez nsys (trace système) pour confirmer que le CPU est le facteur limitant :

Indicateur (depuis nsys) Seuil Signification
Vides GPU entre kernels >30% du temps d'étape Le CPU ne peut pas alimenter le GPU assez vite
Temps API cudaLaunchKernel >10% du temps total Surcharge de lancement de kernel
Plage NVTX _prepare_tp_inputs >50% du temps d'étape La préparation des entrées domine
Utilisation SM du GPU <60% sans bottleneck comm/sync Inférence limitée par le CPU

Si le rapport nsys n'est pas disponible, utilisez une heuristique approximative : si doubler la taille du batch n'augmente pas proportionnellement l'utilisation GPU ou le débit, la surcharge CPU est probablement le bottleneck.

Utiliser les Résultats de la Skill d'Analyse

Si la skill perf-host-analysis a déjà été exécutée, utilisez sa sortie pour ignorer l'étape de confirmation et prioriser les cibles :

  1. Verdict de détection : Si OUI avec host_prep_confirmed, commencez par _prepare_tp_inputs.
  2. Triage NVTX (depuis Root Cause) : Le top_regressing_ops du bloc de données de transmission mappe les noms de plages NVTX aux fonctions source. Profilez d'abord la fonction avec le plus grand delta absolu.
  3. Triage inter-fonction : Quand la plus grande régression NVTX n'est PAS dans _prepare_tp_inputs (par ex. _fetch_new_requests, broadcast_requests, _update_requests), ciblez directement le fichier source de cette fonction au lieu de défaut vers _prepare_tp_inputs. Voir references/trtllm-nvtx-ranges.md pour la correspondance NVTX-vers-source.

Configuration du Profilage

line_profiler (Méthode Principale)

Variables d'environnement :

  • TLLM_LINE_PROFILER_ENABLED=True — Activez le profileur
  • TLLM_LINE_PROFILER_PATH — Chemin du fichier de sortie
  • TLLM_LINE_PROFILER_FUNCTIONS — Fonctions supplémentaires à profiler (séparées par des virgules)

Format de spécification de fonction :

# Méthodes de classe : module.path.ClassName.method_name
TLLM_LINE_PROFILER_FUNCTIONS="tensorrt_llm._torch.pyexecutor.model_engine.PyTorchModelEngine._prepare_tp_inputs"

# Fonctions autonomes : module.path::function_name
TLLM_LINE_PROFILER_FUNCTIONS="tensorrt_llm._torch.pyexecutor.sampler::_group_requests_by_strategy_key"

# Fonctions multiples (séparées par des virgules)
TLLM_LINE_PROFILER_FUNCTIONS="module.Class.method1,module.Class.method2"

Affinité CPU (Facteur d'Environnement)

L'affinité des cœurs CPU peut affecter significativement les mesures de surcharge hôte, particulièrement sur les systèmes multi-socket (par ex. B300). Épingler les processus à des cœurs proches du nœud NUMA du GPU réduit la latence d'accès mémoire inter-socket.

  • Vérifier l'affinité actuelle : taskset -p <pid> ou numactl --show
  • Épingler au nœud NUMA local : numactl --cpunodebind=<node> --membind=<node>
  • Impact : Jusqu'à 2x de différence en surcharge hôte sur les systèmes B300

Lors de la comparaison des résultats de profilage entre les exécutions, assurez-vous que l'affinité CPU est cohérente. Ne modifiez pas l'affinité en externe, sauf si l'utilisateur vous demande de le faire pour examiner les effets de cette partie. Documentez le paramètre d'affinité dans le rapport de chaque round s'il varie.

Gestion de l'Espace de Travail et du Suffixe

Chaque exécution de profilage doit avoir un suffixe unique pour suivre la progression entre les rounds :

EXTRA_SUFFIX=round0_baseline bash profile.sh
EXTRA_SUFFIX=round1_eliminate_redundant_iter bash profile.sh

Boucle d'Optimisation Autonome

Exécutez N rounds (3 par défaut) du cycle suivant :

FOR round = 1 to MAX_ROUNDS:

  1. PROFILE (avec Drill-Down)
  2. ANALYZE (Multi-Option)
  3. OPTIMIZE (Apply Change)
  4. TEST (Unit Test Validation)
  5. VALIDATE (Re-Profile)
  6. REPORT

END FOR → FINAL SUMMARY

Phase 1 : PROFILE (avec Drill-Down)

  • Exécutez la charge de travail avec le profileur activé
  • Parsez la sortie : identifiez les fonctions avec le temps Total le plus élevé et les lignes avec le % Time le plus élevé
  • CRITIQUE : Fouillez dans les sous-fonctions pas encore profilees (voir ci-dessous)

Profilage Drill-Down

Le profileur par défaut couvre les fonctions d'exécuteur de haut niveau mais pas toutes les sous-fonctions. Quand une fonction profileée montre la plupart du temps dans un seul sous-appel, vous devez fouiller.

Quand : Une seule ligne consomme >80% du temps d'une fonction en appelant une sous-fonction non profileée :

Line #      Hits         Time    Per Hit   % Time  Line Contents
==============================================================
  2848      4100  59200000000.0  14439024.4   98.7      output = self.model_engine.forward(...)

Comment :

  1. Identifiez le chemin qualifié complet de la sous-fonction (par ex. tensorrt_llm._torch.pyexecutor.model_engine.PyTorchModelEngine._prepare_tp_inputs)
  2. Ajoutez-le à TLLM_LINE_PROFILER_FUNCTIONS
  3. Reprofilez pour obtenir les données au niveau des lignes à l'intérieur
  4. Analysez maintenant les hotspots internes

Pour les cibles de drill-down courantes, voir references/hot-path-files.md.

Phase 2 : ANALYZE (Multi-Option)

Pour le hotspot choisi :

  1. Identifiez les hotspots supérieurs par temps absolu (pas seulement %) dans la fonction cible
  2. Classifiez chaque hotspot par type. Tableau récapitulatif :
Type Indicateurs Sévérité
SYNC .item(), .cpu(), synchronize() Critique
ALLOC torch.zeros/empty/tensor() dans des boucles, .clone() Haute
PYLOOP for x in collection: avec beaucoup d'itérations Haute
REDUNDANT_ITER Passages multiples sur la même collection Haute
DEAD_WORK Construction d'objet dont les résultats sont toujours ignorés Haute
CONTAINER Recherches Dict/set dans des boucles chaudes Moyenne
FUNCALL Appels de méthode/propriété répétés Moyenne
GIL Contention de verrou/queue Moyenne
GC Pics de latence périodiques, pauses non-déterministes Basse
COMPUTE Calcul réel (peut ne pas être optimisable) Basse

Pour une classification détaillée avec exemples de code, voir references/hotspot-classification.md.

  1. Proposez 2-4 options d'optimisation dans un tableau :
Option Description Économies Estimées Risque Complexité
A ... ... Bas/Moyen/Haut ...
B ... ... ... ...
  1. Sélectionnez la meilleure option et expliquez le raisonnement (préférez économies élevées + risque faible)

Pour les modèles d'optimisation par type, voir references/optimization-patterns.md.

Phase 3 : OPTIMIZE (Apply Change)

  • Appliquez le changement de code sélectionné avec l'outil Edit
  • Une optimisation par round — gardez les changements minimaux et ciblés
  • Enregistrez le changement exact (fichier, plage de lignes, avant/après) pour un potentiel rollback

Phase 4 : TEST (Unit Test Validation)

Obligatoire après chaque optimisation. Trouvez et exécutez les UTs associés pour vérifier la correction.

Trouver les tests associés :

# Recherche par nom de fichier modifié
grep -rl "model_engine\|PyTorchModelEngine" tests/unittest/_torch/executor/

# Recherche par nom de fonction modifié
grep -rl "_prepare_tp_inputs\|prepare_inputs" tests/

Exécuter les tests :

# Exécuter un fichier de test spécifique avec arrêt à la première défaillance
pytest tests/unittest/_torch/executor/test_pytorch_model_engine.py -v -x --timeout=120

# Exécuter une méthode de test spécifique
pytest tests/unittest/_torch/executor/test_pytorch_model_engine.py::PyTorchModelEngineTestCase::test_position_id_preparation -v -x

Pour la correspondance complète UT-vers-fichier, voir references/hot-path-files.md.

Si les tests échouent :

  1. Lisez le message d'erreur
  2. Rollback immédiatement (git checkout -- <file>)
  3. Analysez pourquoi l'optimisation a cassé la correction
  4. Essayez l'option suivante meilleure de la Phase 2

Phase 5 : VALIDATE (Re-Profile)

  • Réexécutez le profileur avec la même charge de travail, en utilisant le suffixe round<N>_<description>
  • Comparez trois choses :
    1. Le temps du hotspot cible a-t-il diminué ?
    2. Le temps Total de la fonction globale a-t-il diminué ?
    3. Les métriques de benchmark (TPOT, débit) ont-elles amélioré ?

Si une régression est détectée (le temps de fonction a augmenté ou les métriques se sont aggravées) :

  • L'« optimisation » a peut-être déclenché un piège CPython — voir references/optimization-patterns.md (section CPython Pitfalls)
  • Rollback et essayez l'option suivante meilleure de la Phase 2

Phase 6 : REPORT

Journal pour ce round :

  • Numéro du round
  • Localisation du hotspot (fichier:ligne) et classification
  • Optimisation appliquée (avec résumé du code avant/après)
  • Delta de temps : temps Total de la fonction avant → après
  • Delta de benchmark : TPOT, débit avant → après

Lecture de la Sortie de Profil

Timer unit: 1e-06 s
Total time: 1.234 s
File: /path/to/file.py
Function: my_function at line 100

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   100                                           def my_function(self):
   101       500    890000.0   1780.0     72.1       result = tensor.item()
   102       500    234567.0    469.1     19.0       return result

Comment lire efficacement :

  1. Commencez par le temps Total pour chaque fonction — c'est le budget global
  2. Triez mentalement les lignes par Time absolu, pas seulement % Time (3% d'une fonction 60s = 1,8s)
  3. Vérifiez le nombre de Hits pour comprendre les modèles d'itération :
    • Hits = 2 × nombre attendu → surcharge de boucle for x in range(1): (2 hits = entrée + vérif sortie)
    • Hits ≫ attendu → la ligne est dans une boucle imbriquée
  4. Cherchez les modèles répétés : si 10 lignes chacune prennent 3% dans un corps de boucle, la boucle elle-même coûte 30%

Critères d'Arrêt

Arrêtez la boucle d'optimisation quand :

  1. Limite d'itération atteinte : N rounds complétés (3 par défaut)
  2. Aucun hotspot actif : Les hotspots supérieurs sont du calcul GPU pur (type COMPUTE)
  3. Rendements décroissants : < 5% d'amélioration dans les 2 derniers rounds
  4. Seuil de risque : Les optimisations supplémentaires nécessitent des changements architecturaux (par ex. Cython, struct-of-arrays)
  5. Défaillances de tests : Impossible de trouver une optimisation qui passe les UTs

Métrique de succès primaire : Débit de benchmark (requêtes/sec ou tokens/sec) tel que mesuré par le script de profilage. Les réductions de temps line_profiler sont des indicateurs avancés, mais le débit est la vérité de base — une accélération au niveau fonction qui n'améliore pas le débit n'est pas une vraie victoire.


Rapport de Synthèse Final

Le rapport final doit inclure :

  • Rounds exécutés : Nombre de cycles profile-optimize complétés
  • Amélioration cumulative : Réduction totale du temps hôte (pourcentage et absolu)
  • Métriques de benchmark : Tableau de comparaison avant/après (TPOT, débit, ITL, E2EL)
  • Optimisations appliquées : Liste de changements avec localisation fichier:ligne et classification
  • Tentatives échouées : Toute optimisation essayée et annulée (avec pourquoi)
  • Hotspots restants : Bottlenecks supérieurs qui n'ont pas pu être optimisés (avec classification)
  • Recommandations : Suivi suggéré pour les changements architecturaux si nécessaire

Pour un exemple multi-round concret, voir references/examples.md.


Fichiers de Référence

Fichier Contenu
references/optimization-patterns.md Catalogue de modèles par type de hotspot + pièges CPython
references/hotspot-classification.md Indicateurs étendus par type et exemples de code
references/hot-path-files.md Tableaux de fichiers clés, cibles drill-down, correspondance UT
references/examples.md Exemples d'utilisation et walkthrough multi-round
trtllm-nvtx-ranges.md Référence de plage NVTX TRT-LLM (depuis la skill d'analyse) — mappe les noms de plages aux fonctions source

Skills similaires