Analyser les graphiques en flammes de profilage du GPU sur Azure Kubernetes Service (AKS) (préversion)

Les graphiques en flammes de profilage du GPU montrent quelles fonctions consomment le plus de mémoire GPU. Cet article explique comment interpréter les graphiques de flamme générés par les outils de profilage GPU pour Azure Kubernetes Service (AKS).

Important

Les fonctionnalités d’évaluation AKS sont disponibles en libre-service et font l’objet d’un abonnement. Les versions préliminaires sont fournies « en l’état » et « selon leur disponibilité » et ne sont pas couvertes par les accords de niveau de service ni par la garantie limitée. Les versions préliminaires d’AKS sont partiellement couvertes par le support client, dans la limite du raisonnable. Par conséquent, ces fonctionnalités ne sont pas destinées à une utilisation en production. Pour plus d’informations, consultez les articles de support suivants :

Avant de commencer, configurez le profilage GPU et vérifiez que les données de profilage sont disponibles dans Pyroscope et Grafana.

Note

La première exécution de profilage peut ne pas afficher chaque symbole, y compris les symboles des opérations de démarrage telles que le chargement du modèle, tandis que le profileur analyse la charge de travail et résout les symboles. Réexécutez la charge de travail après l’analyse initiale pour capturer ces symboles dans les profils suivants.

Comprendre les graphiques de flammes

Un graphe de flamme visualise les piles d’appels profilées. Chaque barre représente une fonction, et les barres empilées indiquent la chaîne d’appels, c’est-à-dire quelle fonction en a appelé une autre. La largeur de chaque barre représente la quantité de la ressource mesurée, telle que le temps processeur ou la mémoire GPU allouée, qui transite par cette fonction.

Règle de clé : plus une barre est large, plus la ressource mesurée transite par cette fonction.

Les exemples de graphiques de flamme suivants sont capturés à partir d’une charge de travail d’inférence vLLM.

Capture d’écran de la vue initiale repliée du diagramme en flammes dans Grafana pour une charge de travail vLLM, montrant des barres empilées qui représentent les piles d’appels d’allocation de mémoire du GPU.

Tip

Utilisez Développer tous les groupes dans le panneau de graphique en flammes de Grafana pour afficher la pile d’appels complète sans repli. Utilisez la zone de recherche pour rechercher des fonctions ou des mots clés spécifiques. Pour éviter que le panneau ne se replie à nouveau, mettez en pause l’actualisation automatique du tableau de bord en réglant l’intervalle d’actualisation sur Désactivé dans le coin supérieur droit du tableau de bord Grafana pendant que vous inspectez le graphique en flammes déplié.

Capture d’écran du diagramme en flammes dans Grafana pour une charge de travail vLLM après avoir sélectionné Développer tous les groupes, affichant la pile d’appels complète avec des cadres de fonction individuels pour les allocations de mémoire GPU.

Tip

Utilisez le bloc Focus pour vous concentrer sur un chemin d’allocation spécifique.

Lire les symboles

Les étiquettes de graphe de flamme suivent les conventions suivantes :

Format de symbole Meaning
Foo > bar class Foo: method def bar()
Foo > __init__ Constructeur de classe Foo
bar (seul) Fonction autonome def bar()
<interpreter trampoline> Surcharge de CPython. Vous pouvez ignorer ce symbole.
<raw-address>, par exemple, 0x7f151 Code C ou CUDA natif sans symbole Python disponible.

Voici quelques exemples :

  • GPUModelRunner / _allocate_kv_cache_tensors: méthode sur une classe. Lisez ce symbole en tant que class GPUModelRunner: def _allocate_kv_cache_tensors(self).
  • LlamaMLP / __init__: constructeur appelé lors de la création d’un LlamaMLP(...) objet.
  • _compile_fx_inner: fonction au niveau du module autonome qui n’est pas à l’intérieur d’une classe.

Comprendre l’allocation propre par rapport à l’allocation totale

Utilisez les valeurs Self et Total pour déterminer où la ressource est consommée :

  • Total est la ressource consommée par une fonction et tout ce qu’elle appelle. Une fonction peut avoir un total élevé sans rien allouer elle-même, parce qu’elle fait partie de la chaîne d’appels.
  • Self désigne les ressources consommées directement par la fonction, à l’exclusion de ses fonctions filles. Une valeur d’auto-valeur élevée signifie que cette fonction est l’endroit où la ressource est consommée.

Par exemple, si GPUModelRunner._allocate_kv_cache_tensors a 55,1 Go d’allocation propre, c’est la fonction qui appelle torch.empty() pour créer les tenseurs du cache de paires clé-valeur (KV).

Utilisez les techniques de navigation suivantes :

  • Commencez par les nœuds feuilles, qui sont des barres sans rien au-dessus de celles-ci. Leur largeur entière représente l’allocation automatique.
  • Ignorez les barres larges avec zéro allocation automatique lorsque vous recherchez la source d’une allocation. Ces fonctions orchestrent les appels à d’autres fonctions.
  • Analysez les barres larges avec une allocation propre élevée comme pistes d’optimisation potentielles.

Rechercher le plus grand consommateur de ressources

Procédez comme suit pour identifier les points d’accès :

  1. Examinez les barres les plus larges en haut du graphique. Ces fonctions feuilles allouent de la mémoire. Plus la barre est large, plus elle consomme de mémoire.

  2. Vérifier l’allocation propre par rapport à l’allocation totale. Une barre large en bas avec self: 0 fait partie de la chaîne d’appels. Suivez-le de bas en haut jusqu’à trouver une barre avec une forte allocation en self.

  3. Lisez la pile des appels de bas en haut pour comprendre pourquoi la fonction a été appelée. L’exemple suivant se termine par la fonction qui effectue l’allocation :

    <raw-address>, for example, 0x7f151       -> native code entry
    <interpreter trampoline>                 -> CPython dispatch
    <module>                                 -> script top level
    EngineCoreProc.run_engine_core           -> vLLM engine startup
    EngineCore.__init__                      -> engine initialization
    EngineCore._initialize_kv_caches         -> KV cache setup
    Worker.initialize_from_config            -> worker setup
    GPUModelRunner.initialize_kv_cache       -> model runner
    GPUModelRunner._allocate_kv_cache_tensors -> actual allocation
    

Utilisez le tableau suivant comme référence rapide :

Goal Ce qu’il faut rechercher
Rechercher ce qui alloue le plus de mémoire Barre feuille la plus large en haut de la pile
Trouvez ce qui utilise le plus de mémoire Barre la plus large en bas de la pile
Identifier les cibles d’optimisation Barres à allocation propre large
Identifier les fonctions que vous pouvez ignorer Barres larges avec zéro allocation automatique

Étapes suivantes