Analysieren von GPU-Profilierungsflammdiagrammen auf Azure Kubernetes Service (AKS) (Vorschau)

GPU-Profiling-Flammendiagramme zeigen, welche Funktionen den meisten GPU-Speicher verbrauchen. In diesem Artikel wird erläutert, wie Sie die Flammendiagramme interpretieren, die von den GPU-Profilerstellungstools für Azure Kubernetes Service (AKS) generiert werden.

Important

AKS-Preview-Funktionen stehen auf Selbstbedienungs- und Opt-in-Basis zur Verfügung. Vorschauversionen werden „im Istzustand“ und „wie verfügbar“ bereitgestellt und sind von den Service Level Agreements und der eingeschränkten Garantie ausgeschlossen. AKS-Vorschauversionen werden teilweise vom Kundensupport auf Grundlage der bestmöglichen Leistung abgedeckt. Daher sind diese Funktionen nicht für die Verwendung in der Produktion vorgesehen. Weitere Informationen finden Sie in den folgenden Supportartikeln:

Bevor Sie beginnen, richten Sie die GPU-Profilerstellung ein , und vergewissern Sie sich, dass Profilerstellungsdaten in Pyroscope und Grafana verfügbar sind.

Note

Beim ersten Profiling-Durchlauf werden möglicherweise nicht alle Symbole angezeigt, einschließlich Symbolen aus Startvorgängen wie dem Laden des Modells, während der Profiler den Workload analysiert und Symbole auflöst. Führen Sie die Workload nach der ersten Analyse erneut aus, um diese Symbole in nachfolgenden Profilen zu erfassen.

Flammendiagramme verstehen

In einem Flammdiagramm werden profilierte Aufrufstapel visualisiert. Jeder Balken stellt eine Funktion dar, und die gestapelten Balken zeigen die Aufrufkette an, oder welche Funktion eine andere Funktion aufgerufen hat. Die Breite der einzelnen Balken stellt die Menge der gemessenen Ressource dar, z. B. CPU-Zeit oder zugeordneter GPU-Speicher, der durch diese Funktion fließt.

Grundregel: Je breiter ein Balken ist, desto mehr von der gemessenen Ressource fließt durch die Funktion.

Die folgenden Beispielflammendiagramme werden aus einer vLLM-Ableitungsworkload erfasst.

Screenshot der anfänglichen reduzierten Flammendiagrammansicht in Grafana für eine vLLM-Workload mit gestapelten Balken, die GPU-Speicherzuordnungs-Aufrufstapel darstellen.

Tip

Verwenden Sie im Flammendiagramm-Bereich von Grafana Alle Gruppen erweitern, um den vollständigen Aufrufstapel ohne Einklappen anzuzeigen. Verwenden Sie das Suchfeld , um bestimmte Funktionen oder Schlüsselwörter zu finden. Um zu verhindern, dass das Panel erneut abstürzt, halten Sie die automatische Aktualisierung des Dashboards an, indem Sie das Aktualisierungsintervall in der oberen rechten Ecke des Grafana-Dashboards auf "Aus " festlegen, während Sie das erweiterte Flammdiagramm überprüfen.

Screenshot des Flammendiagramms in Grafana für eine vLLM-Workload nach Auswahl der Option „Alle Gruppen erweitern“, die den vollständigen Aufrufstapel mit einzelnen Funktions-Frames für GPU-Speicherzuweisungen zeigt.

Tip

Verwenden Sie den Fokusblock , um sich auf einen bestimmten Zuordnungspfad zu konzentrieren.

Lesen der Symbole

Flammdiagrammbeschriftungen folgen den folgenden Konventionen:

Symbolformat Bedeutung
Foo > bar class Foo: method def bar()
Foo > __init__ Konstruktor der Klasse Foo
bar (allein) Eigenständige def bar() Funktion
<interpreter trampoline> CPython-Overhead. Sie können dieses Symbol ignorieren.
<raw-address>, zum Beispiel 0x7f151 Systemeigener C- oder CUDA-Code ohne Python Symbol verfügbar.

Dazu gehören:

  • GPUModelRunner / _allocate_kv_cache_tensors: Eine Methode für eine Klasse. Lesen Sie dieses Symbol als class GPUModelRunner: def _allocate_kv_cache_tensors(self).
  • LlamaMLP / __init__: Ein Konstruktor, der beim Erstellen eines LlamaMLP(...) Objekts aufgerufen wird.
  • _compile_fx_inner: Eine eigenständige Funktion auf Modulebene, die sich nicht innerhalb einer Klasse befindet.

Eigene und Gesamtzuweisung verstehen

Verwenden Sie die Werte "Self " und " Total ", um zu bestimmen, wo die Ressource verbraucht wird:

  • Total ist die Ressource, die von einer Funktion verbraucht wird, und alles, was sie aufruft. Eine Funktion kann eine große Summe aufweisen, aber nichts selbst zuordnen, da sie Teil der Anrufkette ist.
  • Self bezeichnet die Ressourcen, die direkt durch die Funktion verbraucht werden, ausgenommen die ihrer untergeordneten Funktionen. Ein hoher Selbstwert bedeutet, dass diese Funktion an der Stelle ist, an der die Ressource verbraucht wird.

Wenn GPUModelRunner._allocate_kv_cache_tensors beispielsweise eine Eigenallokation von 55,1 GB hat, ist es die Funktion, die torch.empty() aufruft, um die Schlüssel-Wert-Cache-Tensoren (KV) zu erstellen.

Verwenden Sie die folgenden Navigationstechniken:

  • Beginnen Sie bei den Blattknoten; das sind Balken, über denen sich nichts befindet. Ihre gesamte Breite stellt die Selbstzuweisung dar.
  • Überspringen Sie breite Balken mit null Selbstzuweisung, wenn Sie nach der Quelle einer Zuordnung suchen. Diese Funktionen koordinieren Aufrufe anderer Funktionen.
  • Untersuchen Sie breite Balken mit hoher Eigenallokation als potenzielle Optimierungsziele.

Größten Ressourcenverbraucher finden

Führen Sie die folgenden Schritte aus, um Hotspots zu identifizieren:

  1. Sehen Sie sich die breitesten Balken am oberen Rand des Diagramms an. Diese Blattfunktionen weisen Speicher zu. Je breiter die Leiste, desto mehr Arbeitsspeicher verbraucht wird.

  2. Vergleichen Sie die eigene Zuweisung mit der Gesamtzuweisung. Eine breite Leiste am unteren Rand mit self: 0 ist Teil der Aufrufkette. Folgen Sie ihr nach oben, bis Sie einen Balken mit hoher Self-Zuweisung finden.

  3. Lesen Sie den Aufrufstapel von unten nach oben, um zu verstehen, warum die Funktion aufgerufen wurde. Das folgende Beispiel endet mit der Funktion, die die Zuordnung ausführt:

    <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
    

Verwenden Sie die folgende Tabelle als Kurzübersicht:

Zielsetzung Worauf zu achten ist
Ermitteln, was den meisten Arbeitsspeicher zuweist Breiteste Blattleiste am oberen Rand des Stapels
Suchen, was für den größten Arbeitsspeicher verantwortlich ist Breitester Balken unten im Stapel
Identifizieren von Optimierungszielen Balken mit breiter Selbstzuweisung
Identifizieren von Funktionen, die Sie überspringen können Breite Balken ohne eigene Allokation

Nächste Schritte