Fallstudie: Isolieren eines Leistungsproblems (C#, Visual Basic, F#)

Verwenden Sie die Profilerstellungstools, um Leistungsprobleme zu untersuchen und Problembereiche zu isolieren. In dieser Fallstudie wird eine Beispielanwendung verwendet, die Leistungsprobleme aufweist. Es soll veranschaulicht werden, wie Sie Profilerstellungstools zur Steigerung der Effizienz verwenden können. Wenn Sie Profilerstellungstools vergleichen möchten, lesen Sie Welches Tool sollte ich auswählen?

In dieser Fallstudie werden die folgenden Themen behandelt:

  • Verwenden von Visual Studio-Profilerstellungstools, um die Anwendungsleistung zu analysieren.
  • Interpretieren von Daten, die diese Tools bereitstellen, um Leistungsengpässe zu identifizieren.
  • So wenden Sie praktische Strategien zum Optimieren von Code mit Fokus auf .NET-Indikatoren, Anzahl der Aufrufe und Zeiterfassungsdaten an.

Folgen Sie diesen Ratschlägen und wenden Sie diese Techniken anschließend auf Ihre eigenen Anwendungen an, um sie effizienter und kostengünstiger zu gestalten.

Isolieren eines Leistungsproblems – Fallstudie

Die in dieser Fallstudie verwendete Beispielanwendung ist eine ASP.NET-App, die Abfragen für eine simulierte Datenbank ausführt. Für das Beispiel wurde das Diagnostik-Sample herangezogen.

Das ausschlaggebende Problem, das die Beispielanwendung im Hinblick auf die Leistung aufweist, hängt mit ineffizienten Codierungsmustern zusammen. Die Anwendung weist einen Leistungsengpass auf, der ihre Effizienz stark beeinträchtigt. Im Zusammenhang mit diesem Problem können folgende Symptome beobachtet werden:

  • Geringe CPU-Auslastung: Die Anwendung weist eine geringe CPU-Auslastung auf, was bedeutet, dass nicht die CPU der Engpass ist.

  • Hohe Threadanzahl im ThreadPool: Die Threadanzahl ist relativ hoch und nimmt stetig zu. Es wird die Außerkraftsetzung des ThreadPools vorgeschlagen.

  • Lange Anwendungsantwortzeit: Die Anwendung reagiert aufgrund eines Mangels an verfügbaren Threads langsam, was die Verarbeitung neuer Arbeitsaufgaben verzögert.

In dieser Fallstudie soll dieses Problem mithilfe der Profilerstellungstools von Visual Studio behoben werden, die dazu dienen, die Leistung der Anwendung zu analysieren. Sobald die Entwickler verstanden haben, an welcher Stelle und wie die Leistung der Anwendung verbessert werden kann, können sie Optimierungen implementieren, um Code schneller und effizienter zu gestalten. Das ultimative Ziel ist es, die Gesamtleistung der Anwendung zu verbessern, sodass sie effizienter und kostengünstiger ausgeführt werden kann.

Herausforderung

Das Beheben der Leistungsprobleme in der .NET-Beispielanwendung bringt verschiedene Herausforderungen mit sich. Diese Herausforderungen sind auf die komplexe Diagnose von Leistungsengpässen zurückzuführen. Im Folgenden sind die größten Herausforderungen aufgeführt, die die Behebung der beschriebenen Probleme mit sich bringen kann:

  • Diagnose von Leistungsengpässen: Eine der größten Herausforderungen besteht in der genauen Identifizierung der Ursachen der Leistungsprobleme. Eine geringe CPU-Auslastung in Kombination mit geringer Leistung kann auf mehrere Faktoren zurückzuführen sein. Developer müssen Profilerstellungstools effektiv verwenden, um diese Probleme zu diagnostizieren. Dies erfordert ein gewisses Verständnis davon, wie diese Tools funktionieren und wie ihre Ausgabe interpretiert werden kann.

  • Wissens- und Ressourceneinschränkungen: Es kann vorkommen, dass Teams nicht genügend Wissen, Fachwissen und Ressourcen haben. Die Profilerstellung und Optimierung einer Anwendung erfordert spezifische Fähigkeiten und Erfahrungen. Jedoch verfügen möglicherweise nicht alle Teams über sofortigen Zugriff auf diese Ressourcen.

Um diese Herausforderungen zu bewältigen, bedarf es eines strategischen Ansatzes, dessen Fokus auf der effektiven Verwendung der Profilerstellungstools, auf dem technischen Wissen, auf einer sorgfältigen Planung und auf Tests liegt. Das Ziel dieser Fallstudie ist es, Entwickler*innen durch diesen Prozess zu führen, Strategien und Erkenntnisse bereitzustellen, um diese Herausforderungen zu überwinden und die Leistung der Anwendung zu verbessern.

Strategie

Hier ist ein allgemeiner Überblick über den Ansatz in dieser Fallstudie:

  • Wir beginnen mit der Untersuchung, indem wir die .NET-Indikatormetriken beobachten und zugleich Leistungsdaten sammeln. Das Tool .NET-Indikatoren von Visual Studio ist, ebenso wie das CPU-Auslastungstool ein guter Ausgangspunkt zur Leistungsuntersuchung.
  • Um weitere Erkenntnisse zu erhalten, die dabei helfen, Probleme zu isolieren oder die Leistung zu verbessern, erwägen Sie als Nächstes eine Ablaufverfolgung mit einem der anderen Profilerstellungstools. Verwenden Sie das Tool zur Instrumentierung, um z. B. die Anzahl der Aufrufe und die Zeiterfassungsdaten zu untersuchen.

Um Daten zu erfassen, müssen die folgenden Aufgaben ausgeführt werden:

  • Konfigurieren der App auf einen Releasebuild.
  • Wählen Sie im Leistungs-Profiler das CPU-Auslastungs-Tool (ALT+F2) aus. (Bei späteren Schritten kommt das Instrumentierungs-Tool zum Einsatz.)
  • Starten Sie die App im Leistungsprofiler, und erfassen Sie eine Ablaufverfolgung.

Überprüfen Sie die Leistungsindikatoren

Während die App ausgeführt wird, beobachten wir die Indikatoren im .NET-Indikatortool. Bei den ersten Untersuchungen sollten Sie einige wichtige Metriken im Auge behalten:

  • CPU Usage. Sehen Sie sich diesen Leistungsindikator an, um festzustellen, ob bei hoher oder geringer CPU-Auslastung Leistungsprobleme auftreten. Das kann ein Hinweis auf bestimmte Arten von Leistungsproblemen sein. Beispiel:
    • Verwenden Sie bei hoher CPU-Auslastung das CPU-Auslastungstool, um Bereiche zu identifizieren, in denen wir Code möglicherweise optimieren können. Eine Anleitung dazu finden Sie unter Fallstudie: Leitfaden für Anfänger zur Optimierung von Code.
    • Verwenden Sie bei geringer CPU-Auslastung das Instrumentierungs-Tool, um die Anzahl der Aufrufe und die durchschnittliche Funktionszeit auf der Grundlage der Wanduhrzeit zu ermitteln. Dies kann hilfreich sein, wenn Sie Probleme identifizieren möchten (z. B. Konflikte oder Threadpoolmangel).
  • Allocation Rate. Bei einer Web-App, die Anfragen bearbeitet, sollte die Rate relativ konstant sein.
  • GC Heap Size. Sehen Sie sich diesen Zähler an, um zu sehen, ob die Speicherauslastung kontinuierlich wächst und es mögliche Verluste gibt. Bei einer eher hohen Auslastung verwenden Sie eines der Tools zur Arbeitsspeicherauslastung.
  • Threadpool Thread Count. Für eine Web-App, die Anfragen bedient, können Sie diesen Zähler beobachten, um herauszufinden, ob die Threadanzahl konstant bleibt oder gleichmäßig ansteigt.

Dieses Beispiel zeigt, wie es aussieht, wenn CPU Usage niedrig und ThreadPool Thread Count relativ hoch ist.

Screenshot von Leistungsindikatoren, die im .NET-Zähler-Tool angezeigt werden.

Eine gleichmäßig steigende Threadanzahl mit einer niedrigen CPU-Auslastung kann ein Indikator für Mängel im Threadpool sein. Der Threadpool ist gezwungen, ständig neue Threads zu erstellen. Ein Mangel im Threadpool tritt auf, wenn im Pool keine Threads zum Verarbeiten neuer Arbeitselemente verfügbar sind. Dies führt häufig dazu, dass Anwendungen langsam reagieren.

Ausgehend von der geringen CPU-Auslastung, der relativ hohen Threadanzahl und der Theorie eines möglichen Mangels im Threadpool, sollten Sie zur Verwendung des Instrumentierungs-Tools übergehen.

Untersuchen von Anrufanzahlen und Zeitdaten

Sehen wir uns eine Ablaufverfolgung aus dem Instrumentierungs-Tool an, um mehr darüber herauszufinden, was mit den Threads los ist.

Nachdem wir die Ablaufverfolgung mithilfe des Instrumentierungstools erfasst und in Visual Studio geladen haben, überprüfen wir die anfängliche .diagsession-Berichtseite, auf der die zusammengefassten Daten angezeigt werden. In der erfassten Ablaufverfolgung verwenden wir den im Bericht aufgeführten Link Details öffnen und wählen dann Flammendiagramm aus.

Screenshot eines Flame-Diagramms im Instrumentierungs-Tool.

Das Flammendiagramm zeigt uns, dass die Anwendungslaufzeit erheblich durch die Funktion QueryCustomerDB (gelb) beeinflusst wird.

Klicken Sie mit der rechten Maustaste auf die QueryCustomerDB-Funktion und wählen Sie Ansicht in Aufrufstruktur aus.

Screenshot einer Aufrufstruktur im Instrumentierungs-Tool.

Der heiße Pfad zeigt den Codepfad mit der höchsten CPU-Auslastung in der App an. Mithilfe des Flammensymbols für den „heißen Pfad“ (Screenshot des Symbols für den „heißen Pfad“.) können Leistungsfehler, die sich möglicherweise beheben lassen, schnell identifiziert werden.

In der Ansicht Aufrufstruktur können Sie sehen, dass der heiße Pfad die Funktion QueryCustomerDB enthält, was auf ein potenzielles Leistungsproblem hinweist.

Im Verhältnis zu der Zeit, die in anderen Funktionen verbracht wurde, sind die Werte Selbst und Durchschn. Selbst für die QueryCustomerDB-Funktion sehr hoch. Im Gegensatz zu Gesamt und Durchschn. Gesamt schließen die Selbst-Werte die in anderen Funktionen verbrachte Zeit aus. Deshalb ist dies ein guter Ort, um nach dem Leistungsengpass zu suchen.

Tipp

Wenn die Selbst-Werte im Verhältnis eher niedrig statt hoch waren, sollten Sie wahrscheinlich die tatsächlichen Abfragen überprüfen, die von der QueryCustomerDB-Funktion getätigt werden.

Doppelklicken Sie auf die QueryCustomerDB-Funktion, um den Quellcode für die Funktion anzuzeigen.

public ActionResult<string> QueryCustomerDB()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

Wir recherchieren ein wenig. Alternativ können wir Zeit sparen und Copilot mit der Recherche beauftragen.

Wenn Sie Copilot verwenden, wählen wir Copilot fragen aus dem Kontextmenü und geben die folgende Frage ein:

Can you identify a performance issue in the QueryCustomerDB method?

Tipp

Sie können Slash-Befehle wie /optimize verwenden, um gute Fragen für Copilot zu bilden.

Copilot teilt uns mit, dass dieser Code eine asynchrone API aufruft, ohne await zu verwenden. Hierbei handelt es sich um das Codemuster sync-vor-async, das eine häufige Ursache für Mängel im Threadpool ist, und manchmal Threads blockiert.

Verwenden Sie „Abwarten“, um dieses Problem zu lösen. In diesem Beispiel gibt Copilot den folgenden Codevorschlag zusammen mit der Erklärung.

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

Wenn Sie Leistungsprobleme im Zusammenhang mit Datenbankabfragen feststellen, können Sie mit dem Datenbank-Tool untersuchen, ob bestimmte Aufrufe langsamer sind. Diese Daten können Möglichkeiten zum Optimieren der Abfragen nahelegen. Eine Anleitung dazu, wie Sie ein Leistungsproblem mithilfe des Datenbanktools untersuchen, finden Sie unter Fallstudie: Leitfaden für Anfänger zum Optimieren von Code. Das Datenbank-Tool unterstützt .NET Core mit wahlweise ADO.NET oder Entity Framework Core.

Um in Visual Studio Visualisierungen des Verhaltens einzelner Threads zu erhalten, können Sie beim Debuggen das Fenster Parallele Stapel verwenden. In diesem Fenster werden einzelne Threads zusammen mit Informationen über wartende Threads, Threads, auf die sie warten und Deadlocks angezeigt.

Weitere Informationen zu Mängeln in Threadpools finden Sie unter Erkennen von Threadpool-Mangel.

Nächste Schritte

Die folgenden Artikel und Blogbeiträge enthalten weitere Informationen, die Ihnen helfen, die Visual-Studio-Leistungstools effektiv zu verwenden.