Leitfaden für Einsteiger zur Optimierung von Code und zur Senkung von Rechenkosten (C#, Visual Basic, C++, F#)

Wenn Sie Ihre Computezeit verringern, können Sie Kosten reduzieren. Durch die Optimierung Ihres Codes können Sie also Geld sparen. In diesem Artikel zeigen wir Ihnen, wie Sie diese Aufgabe über verschiedene Profilerstellungstools durchführen können.

Anstatt Schritt-für-Schritt-Anleitungen bereitzustellen, soll Ihnen hier die effektive Verwendung der Profilerstellungstools und die Interpretation der Daten gezeigt werden. Mit dem CPU-Auslastungstool können Sie erfassen und visualisieren, wo Computeressourcen in Ihrer Anwendung verwendet werden. Die CPU-Auslastungsansichten wie die Aufrufstruktur und das Flame-Diagramm zeigen in ansprechender grafischer Visualisierung, wo in Ihrer App Zeit beansprucht wird. Darüber hinaus können automatische Erkenntnisse präzise Optimierungen zeigen, die große Auswirkungen haben können. Andere Profilerstellungstools können Ihnen auch helfen, Probleme zu isolieren. Einen Vergleich der Tools finden Sie unter Welches Tool sollte ich auswählen?

Starten einer Untersuchung

  • Starten Sie Ihre Untersuchung, indem Sie eine CPU-Auslastungs-Ablaufverfolgung durchführen. Das CPU-Auslastungstool ist häufig hilfreich, um Leistungsuntersuchungen zu starten und Code zu optimieren, um Kosten zu senken.
  • Wenn Sie zusätzliche Erkenntnisse benötigen, um Probleme zu isolieren oder die Leistung zu verbessern, sollten Sie als Nächstes eine Ablaufverfolgung mithilfe eines der anderen Profilerstellungstools erfassen. Beispiel:
    • Sehen Sie sich die Speicherauslastung an. Probieren Sie für .NET zuerst das .NET-Objektzuordnungstool aus. Für .NET oder C++ können Sie das Speicherauslastungstool nutzen.
    • Wenn Ihre App Datei-E/A verwendet, verwenden Sie das Datei-E/A-Tool.
    • Wenn Sie ADO.NET oder Entity Framework verwenden, können Sie das Datenbanktool ausprobieren, um SQL-Abfragen, genaue Abfragezeit usw. zu untersuchen.

Beispiel einer Datensammlung

Die in diesem Artikel gezeigten Beispielscreenshots basieren auf einer .NET-App, die Abfragen für eine Datenbank mit Blogs und zugehörigen Blogbeiträgen ausführt. Sie untersuchen zunächst eine Ablaufverfolgung für die CPU-Auslastung, um nach Möglichkeiten zur Optimierung von Code und Reduzierung der Computekosten zu suchen. Sobald Sie eine allgemeine Vorstellung davon haben, was passiert, sehen Sie sich auch Ablaufverfolgungen aus anderen Profilerstellungstools an, um Probleme zu isolieren.

Für die Datensammlung sind die folgenden Schritte erforderlich (hier nicht dargestellt):

  • Legen Sie Ihre App auf einen Releasebuild fest.
  • Wählen Sie im Leistungsprofiler das CPU-Auslastungstool (ALT+F2) aus. (Spätere Schritte umfassen einige der anderen Tools.)
  • Starten Sie die App im Leistungsprofiler, und erfassen Sie eine Ablaufverfolgung.

Untersuchen von Bereichen mit hoher CPU-Auslastung

Erfassen Sie zunächst eine Ablaufverfolgung mit dem CPU-Auslastungstool. Wenn die Diagnosedaten geladen werden, überprüfen Sie zuerst die erste DIAGSESSION-Berichtsseite mit „Wichtigsten Erkenntnissen“ und dem „langsamsten Pfad“. Der „langsamste Pfad“ zeigt den Codepfad mit der höchsten CPU-Auslastung in Ihrer App an. Diese Abschnitte enthalten möglicherweise Tipps, mit denen Sie schnell Leistungsprobleme erkennen können, die Sie verbessern können.

Sie können den „langsamsten Pfad“ auch in der Aufrufstrukturansicht anzeigen. Um diese Ansicht zu öffnen, verwenden Sie den Link Details öffnen im Bericht, und wählen Sie dann Aufrufstruktur aus.

In dieser Ansicht wird wieder der „langsamste Pfad“ angezeigt, der eine hohe CPU-Auslastung für die GetBlogTitleX-Methode in der App anzeigt, wobei etwa 60 % der CPU-Auslastung der App verwendet werden. Allerdings ist der Eigen-CPU-Wert für GetBlogTitleX niedrig, nur etwa 0,10 %. Im Gegensatz zu CPU gesamt schließt der Eigen-CPU-Wert die in anderen Funktionen verbrachte Zeit aus, sodass wir wissen, dass der eigentliche Engpass weiter unten in der Ansicht „Aufrufstruktur“ zu finden ist.

Screenshot: Ansicht „Aufrufstruktur“ im CPU-Auslastungstool

GetBlogTitleX führt externe Aufrufe an zwei LINQ-DLLs aus, die die meiste CPU-Zeit beanspruchen, wie aus den sehr hohen Eigen-CPU-Werten ersichtlich ist. Dies ist der erste Hinweis, dass Sie möglicherweise nach einer LINQ-Abfrage als zu optimierendem Bereich suchen sollten.

Screenshot der Aufrufstrukturansicht im Tool CPU-Auslastung mit hervorgehobener eigener CPU.

Um eine visualisierte Aufrufstruktur und eine andere Ansicht der Daten zu erhalten, wechseln Sie zur Ansicht Flammendiagramm (aus derselben Liste wie die Aufrufstruktur auswählen). Auch hier sieht es so aus, als sei die GetBlogTitleX-Methode für einen Großteil der CPU-Auslastung der App verantwortlich ist (gelb dargestellt). Externe Aufrufe der LINQ-DLLs werden unter dem GetBlogTitleX-Feld angezeigt, und sie verwenden die gesamte CPU-Zeit für die Methode.

Screenshot: Ansicht „Flammendiagramm“ im CPU-Auslastungstool

Sammeln weiterer Daten

Häufig können andere Tools zusätzliche Informationen bereitstellen, um die Analyse zu unterstützen und das Problem einzugrenzen. Da wir beispielsweise die LINQ-DLLs identifiziert haben, probieren wir zuerst das Datenbanktool aus. Sie können dieses Tool zusammen mit der CPU-Auslastung mehrfach auswählen. Wenn Sie eine Ablaufverfolgung gesammelt haben, wählen Sie auf der Diagnoseseite die Registerkarte Abfragen aus.

Auf der Registerkarte „Abfragen“ für die Datenbankablaufverfolgung sehen Sie in der ersten Zeile die längste Abfrage, 2446 ms. Die Spalte Datensätze zeigt an, wie viele Datensätze die Abfrage liest. Wir können diese Informationen für einen späteren Vergleich verwenden.

Screenshot: Datenbankabfragen im Datenbanktool

Wenn Sie die von LINQ generierte SELECT-Anweisung in der Spalte „Query“ untersuchen, identifizieren Sie die erste Zeile als die Abfrage, die der GetBlogTitleX-Methode zugeordnet ist. Um die vollständige Abfragezeichenfolge anzuzeigen, erweitern Sie bei Bedarf 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 Sie hier viele Spaltenwerte abrufen, möglicherweise mehr als Sie benötigen.

Um zu sehen, was mit der App in Bezug auf die Speichernutzung passiert, sammeln Sie eine Stapelüberwachung mit dem .NET Objektzuordnungstool (für C++ verwenden Sie stattdessen das Speicherauslastungstool). Die Ansicht Aufrufstruktur in der Speicherablaufverfolgung zeigt den „langsamsten Pfad“ an und hilft Ihnen, einen Bereich mit hoher Speicherauslastung zu identifizieren. Keine Überraschung: Die GetBlogTitleX-Methode scheint viele Objekte zu generieren! Tatsächlich über 900.000 Objektzuordnungen.

Screenshot: Ansicht „Aufrufstruktur“ im .NET-Objektzuordnungstool

Die meisten erstellten Objekte sind Zeichenfolgen, Objektarrays und Int32-Objekte. Möglicherweise können Sie sehen, wie diese Typen generiert werden, indem Sie den Quellcode untersuchen.

Code optimieren

Es ist an der Zeit, einen Blick auf den GetBlogTitleX-Quellcode zu werfen. Klicken Sie im .NET-Objektzuordnungstool mit der rechten Maustaste auf die Methode, und wählen Sie Zur Quelldatei wechseln aus. Im Quellcode für GetBlogTitleX finden 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 Blogs mit „Fred Smith“ als Autor zu durchsuchen. Bei der Betrachtung sehen Sie, 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 der Beiträge, z. B. Blog-ID.

Sie recherchieren ein wenig, finden einige allgemeine Empfehlungen zum Optimieren von LINQ-Abfragen und gelangen zu diesem Code.

foreach (var x in db.Posts.Where(p => p.Author.Contains("Fred Smith")).Select(b => b.Title).ToList())
{
  Console.WriteLine("Post: " + x);
}

In diesem Code haben Sie mehrere Änderungen vorgenommen, um die Abfrage zu optimieren:

  • Fügen Sie die Where-Klausel hinzu, und entfernen Sie eine der foreach-Schleifen.
  • Bilden Sie nur die Eigenschaft „Title“ in der Select-Anweisung ab. Sie ist die einzige, die in diesem Beispiel benötigt wird.

Führen Sie als Nächstes erneut Tests mit den Profilerstellungstools aus.

Prüfen der Ergebnisse

Führen Sie nach dem Aktualisieren des Codes das CPU-Auslastungstool erneut aus, um eine Ablaufverfolgung zu erfassen. Die Ansicht Aufrufstruktur zeigt, dass GetBlogTitleX nur 1.754 ms lang ausgeführt wird, wobei 37 % der gesamten CPU-Zeit der App verwendet werden, eine deutliche Verbesserung gegenüber 59 %.

Screenshot: Verbesserte CPU-Auslastung in der Ansicht „Aufrufstruktur“ des CPU-Auslastungstools

Wechseln Sie zur Ansicht Flammendiagramm, um eine weitere Visualisierung der Verbesserung anzuzeigen. In dieser Ansicht verwendet GetBlogTitleX auch einen kleineren Teil der CPU.

Screenshot: Verbesserte CPU-Auslastung in der Ansicht „Flammendiagramm“ des CPU-Auslastungstools

Überprüfen Sie die Ergebnisse in der Datenbanktool-Ablaufverfolgung, und mit dieser Abfrage werden nur zwei Datensätze gelesen, anstatt 100.000! Außerdem ist die Abfrage stark vereinfacht und macht das zuvor generierte LEFT JOIN überflüssig.

Screenshot: Schnellere Abfragezeit im Datenbanktool

Überprüfen Sie als Nächstes erneut die Ergebnisse im .NET-Objektzuordnungstool, und beachten Sie, dass GetBlogTitleX nur für 56.000 Objektzuordnungen verantwortlich ist, was gegenüber 900.000 einer Verringerung von fast 95 % entspricht!

Screenshot: Reduzierte Speicherzuweisungen im .NET-Objektzuordnungstool

Durchlaufen

Möglicherweise sind mehrere Optimierungen erforderlich, und Sie können mit Iterationen mit Codeänderungen fortfahren, um festzustellen, welche Änderungen die Leistung verbessern und Ihre Computekosten senken.

Nächste Schritte

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