Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Durch die Optimierung des Codes wird die Berechnungszeit und die Kosten reduziert. In dieser Fallstudie wird die Verwendung von Visual Studio-Profilerstellungstools zum Identifizieren und Beheben von Leistungsproblemen in einer .NET-Beispielanwendung veranschaulicht. Wenn Sie Profilerstellungstools vergleichen möchten, lesen Sie Welches Tool sollte ich auswählen?
In diesem Leitfaden werden folgende Themen behandelt:
- Verwenden von Visual Studio-Profilerstellungstools zum Analysieren und Verbessern der Leistung.
- Praktische Strategien zur Optimierung der CPU-Auslastung, Speicherzuweisung und Datenbankinteraktionen.
Wenden Sie diese Techniken an, um Ihre eigenen Anwendungen effizienter zu gestalten.
Optimierungsfallstudie
Die .NET-Beispielanwendung führt Abfragen für eine SQLite-Datenbank mit Blogs und Beiträgen mithilfe von Entity Framework aus. Es führt viele Abfragen aus und simuliert ein reales Datenabrufszenario. Die App basiert auf dem Beispiel für die ersten Schritte von Entity Framework, verwendet jedoch ein größeres Dataset.
Zu den wichtigsten Leistungsproblemen gehören:
- Hohe CPU-Auslastung: Ineffiziente Berechnungen oder Verarbeitungsaufgaben erhöhen den CPU-Verbrauch und die Kosten.
- Ineffiziente Speicherzuordnung: Schlechte Speicherverwaltung führt zu übermäßiger Garbage Collection und reduzierter Leistung.
- Datenbankaufwand: Ineffiziente Abfragen und übermäßige Datenbankaufrufe beeinträchtigen die Leistung.
In dieser Fallstudie werden Visual Studio-Profilerstellungstools zum Anheften und Beheben dieser Probleme verwendet, um die Anwendung effizienter und kostengünstiger zu gestalten.
Herausforderung
Das Beheben dieser Leistungsprobleme erfordert mehrere Herausforderungen:
- Diagnoseengpässe: Das Identifizieren von Ursachen für hohen CPU-, Arbeitsspeicher- oder Datenbankaufwand erfordert eine effektive Verwendung von Profilerstellungstools und eine korrekte Interpretation der Ergebnisse.
- Wissen und Ressourceneinschränkungen: Profilerstellung und Optimierung erfordern spezifische Fähigkeiten und Erfahrungen, die möglicherweise nicht immer verfügbar sind.
Ein strategischer Ansatz, der Profilerstellungstools, technische Kenntnisse und sorgfältige Tests kombiniert, ist unerlässlich, um diese Herausforderungen zu überwinden.
Strategie
Hier ist eine generelle Übersicht der Vorgehensweise in dieser Fallstudie:
- Beginnen Sie mit einer CPU-Auslastungsablaufverfolgung mit dem CPU-Auslastungstool von Visual Studio. Das Tool für die CPU-Auslastung von Visual Studio ist ein guter Ausgangspunkt für Leistungsuntersuchungen.
- Sammeln zusätzlicher Traces zur Analyse der Speicherauslastung und Datenbankanalyse:
- Verwenden Sie das .NET-Objektzuweisungstool für Speichererkenntnisse.
- Verwenden Sie das Datenbanktool , um SQL-Abfragen und -Anzeigedauern zu untersuchen.
Für die Datensammlung sind die folgenden Aufgaben erforderlich:
- Legen Sie die App auf release build fest.
- Wählen Sie das CPU-Nutzungstool im Performance Profiler (ALT+F2) aus.
- Starten Sie die App im Leistungsprofiler, und erfassen Sie eine Ablaufverfolgung.
Untersuchen von Bereichen mit hoher CPU-Auslastung
Nachdem wir die Ablaufverfolgung mithilfe des Tools für die CPU-Auslastung erfasst und in Visual Studio geladen haben, überprüfen wir die anfängliche .diagsession-Berichtseite, auf der die zusammengefassten Daten angezeigt werden. Verwenden Sie den Link " Details öffnen " im Bericht.
Öffnen Sie in der Berichtsdetailsansicht die Anrufstrukturansicht . Der Codepfad mit der höchsten CPU-Auslastung in der App wird als hot pathbezeichnet. Das Symbol "Hot Path-Flamme" (
) kann dazu beitragen, Leistungsprobleme schnell zu erkennen, die möglicherweise verbessert werden.
In der Aufrufstruktur-Ansicht sehen Sie eine hohe CPU-Auslastung für die GetBlogTitleX-Methode in der App, die etwa 60 % der CPU-Auslastung der App ausmacht. Allerdings ist der Eigen-CPU-Wert für GetBlogTitleX niedrig, nur etwa 0,10 %. Im Gegensatz zur Gesamt-CPU schließt der Self-CPU-Wert die in anderen Funktionen aufgewendete Zeit aus, sodass wir wissen, dass wir tiefer im Aufrufbaum nach dem tatsächlichen Engpass suchen müssen.
GetBlogTitleX führt externe Aufrufe an zwei LINQ-DLLs durch, die die meiste CPU-Zeit verwenden, wie durch die sehr hohen Selbst-CPU-Werte belegt. Dies ist der erste Hinweis darauf, dass eine LINQ-Abfrage möglicherweise ein Bereich ist, der optimiert werden kann.
Um eine visualisierte Anrufstruktur und eine andere Ansicht der Daten zu erhalten, öffnen Sie die Flame Graph-Ansicht . (Oder klicken Sie mit der rechten Maustaste GetBlogTitleX , und wählen Sie "In Flammendiagramm anzeigen" aus.) Hier sieht es wieder so aus, als ob die GetBlogTitleX Methode für die CPU-Auslastung der App verantwortlich ist (in Gelb dargestellt). Externe Aufrufe der LINQ-DLLs werden unterhalb des GetBlogTitleX Felds angezeigt, und sie verwenden die gesamte CPU-Zeit für die Methode.
Sammeln zusätzlicher Daten
Häufig können andere Tools zusätzliche Informationen bereitstellen, um die Analyse zu unterstützen und das Problem zu isolieren. In dieser Fallstudie gehen wir wie folgt vor:
- Sehen Sie sich zunächst die Speicherauslastung an. Es kann eine Korrelation zwischen hoher CPU-Auslastung und hoher Arbeitsspeicherauslastung geben, daher kann es hilfreich sein, beides zu betrachten, um das Problem zu isolieren.
- Da wir die LINQ-DLLs identifiziert haben, sehen wir uns auch das Datenbanktool an.
Überprüfen der Speicherauslastung
Um zu sehen, was mit der App im Hinblick auf die Speicherauslastung passiert, sammeln wir eine Ablaufverfolgung mithilfe des .NET Object Allocation-Tools (Für C++ können Sie stattdessen das Tool für die Speicherauslastung verwenden). Die Anrufstrukturansicht in der Speicherablaufverfolgung zeigt den heißen Pfad an und hilft uns, einen Bereich mit hoher Speicherauslastung zu identifizieren. Keine Überraschung an diesem Punkt, die GetBlogTitleX Methode scheint viele Objekte zu generieren! Über 900.000 Objektzuweisungen tatsächlich.
Die meisten erstellten Objekte sind Zeichenfolgen, Objektarrays und Int32s. Möglicherweise können wir sehen, wie diese Typen durch Untersuchen des Quellcodes generiert werden.
Überprüfen der Abfrage im Datenbanktool
Im Performance Profiler wählen wir das Datenbanktool anstelle der CPU-Auslastung (oder beides) aus. Nachdem eine Ablaufverfolgung erfasst wurde, öffnen Sie auf der Diagnoseseite die Registerkarte Abfragen. Auf der Registerkarte "Abfragen" für die Datenbankablaufverfolgung können Sie sehen, dass die erste Zeile die längste Abfrage mit 2446 ms anzeigt. In der Spalte "Datensätze " wird gezeigt, wie viele Datensätze die Abfrage liest. Sie können diese Informationen für einen späteren Vergleich verwenden.
Durch Untersuchen der SELECT von LINQ in der Spalte "Abfrage" generierten Anweisung identifizieren wir die erste Zeile als die Abfrage, die der GetBlogTitleX Methode zugeordnet ist. Um die vollständige Abfragezeichenfolge anzuzeigen, erweitern Sie die Spaltenbreite. Die vollständige Abfragezeichenfolge lautet:
SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"
Beachten Sie, dass die App hier viele Spaltenwerte abruft, vielleicht mehr als wir benötigen. Sehen wir uns den Quellcode an.
Optimieren von Code
Es ist an der Zeit, sich den GetBlogTitleX Quellcode anzusehen. Klicken Sie im Datenbanktool mit der rechten Maustaste auf die Abfrage, und wählen Sie "Zur Quelldatei wechseln" aus. Im Quellcode für GetBlogTitleXfinden wir den folgenden Code, der LINQ zum Lesen der Datenbank verwendet.
foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
{
foreach (var post in blog.Posts)
{
if (post.Author == "Fred Smith")
{
Console.WriteLine($"Post: {post.Title}");
}
}
}
Dieser Code verwendet foreach Schleifen, um die Datenbank nach beliebigen Blogs mit "Fred Smith" als Autor zu durchsuchen. Wenn Sie dies betrachten, können Sie sehen, dass viele Objekte im Arbeitsspeicher generiert werden: ein neues Objektarray für jeden Blog in der Datenbank, zugeordnete Zeichenfolgen für jede URL und Werte für Eigenschaften, die in den Beiträgen enthalten sind, z. B. Blog-ID.
Wir recherchieren ein wenig und finden einige häufige Empfehlungen für die Optimierung von LINQ-Abfragen. Alternativ können wir Zeit sparen und Copilot die Forschung für uns durchführen lassen.
Wenn wir Copilot verwenden, wählen wir "Copilot fragen" aus dem Kontextmenü aus, und geben Sie die folgende Frage ein:
Can you make the LINQ query in this method faster?
Tipp
Sie können Slash-Befehle wie /optimize verwenden, um gute Fragen für Copilot zu bilden.
In diesem Beispiel gibt Copilot die folgenden vorgeschlagenen Codeänderungen zusammen mit einer Erläuterung.
public void GetBlogTitleX()
{
var posts = db.Posts
.Where(post => post.Author == "Fred Smith")
.Select(post => post.Title)
.ToList();
foreach (var postTitle in posts)
{
Console.WriteLine($"Post: {postTitle}");
}
}
Dieser Code enthält mehrere Änderungen, um die Abfrage zu optimieren:
- Die
WhereKlausel wurde hinzugefügt, und eine derforeachSchleifen wurde entfernt. - Projiziert nur die Title-Eigenschaft in der Anweisung
Select. Das ist alles, was wir in diesem Beispiel brauchen.
Als Nächstes testen wir die Profilerstellungstools erneut.
Ergebnisse
Nach dem Aktualisieren des Codes führen wir das CPU-Leistungstool erneut aus, um eine Aufzeichnung zu erstellen. Die Anrufstrukturansicht zeigt, dass GetBlogTitleX nur 1754 ms läuft und 37% der gesamten CPU-Leistung der App verwendet, was eine erhebliche Verbesserung gegenüber 59%darstellt.
Wechseln Sie zur Flame Graph-Ansicht , um eine weitere Visualisierung anzuzeigen, die die Verbesserung zeigt. In dieser Ansicht nutzt GetBlogTitleX auch einen kleineren Teil der CPU.
Überprüfen Sie die Ergebnisse im Datenbank-Tool-Trace, und es werden nur zwei Datensätze mit dieser Abfrage gelesen, statt 100.000! Außerdem ist die Abfrage stark vereinfacht und macht das zuvor generierte LEFT JOIN überflüssig.
Als Nächstes überprüfen wir die Ergebnisse im .NET Object Allocation-Tool und sehen, dass GetBlogTitleX nur für 56.000 Objektzuweisungen verantwortlich ist, fast eine Reduzierung von 95% von 900.000!
Durchlaufen
Möglicherweise sind mehrere Optimierungen erforderlich, und wir können weiterhin Codeänderungen iterieren, um zu sehen, welche Änderungen die Leistung verbessern und dazu beitragen, die Rechenkosten zu reduzieren.
Nächste Schritte
Die folgenden Artikel und Blogbeiträge enthalten weitere Informationen, die Ihnen helfen, die Visual Studio-Leistungstools effektiv zu verwenden.
- Fallstudie: Isolieren eines Leistungsproblems
- Fallstudie: Doppelte Leistung in weniger als 30 Minuten
- Verbessern der Visual Studio-Leistung mit dem neuen Instrumentierungstool