Przewodnik dla początkujących dotyczący optymalizowania kodu i obniżania kosztów obliczeń (C#, Visual Basic, C++, F#)

Skrócenie czasu obliczeniowego oznacza zmniejszenie kosztów, dzięki czemu optymalizacja kodu może zaoszczędzić pieniądze. W tym artykule pokazano, jak można użyć różnych narzędzi profilowania, aby ułatwić wykonanie tego zadania.

Zamiast dostarczać instrukcje krok po kroku, celem jest pokazanie, jak efektywnie używać narzędzi profilowania i jak interpretować dane. Narzędzie Użycie procesora CPU może ułatwić przechwytywanie i wizualizowanie miejsca użycia zasobów obliczeniowych w aplikacji. Widoki użycia procesora CPU, takie jak drzewo wywołań i wykres płomienia, zapewniają miłą graficzną wizualizację miejsca, w którym spędza się czas w aplikacji. Ponadto automatyczne szczegółowe informacje mogą pokazywać precyzyjne optymalizacje, które mogą mieć duży wpływ. Inne narzędzia profilowania mogą również pomóc w izolowaniu problemów. Aby porównać narzędzia, zobacz Jakie narzędzie należy wybrać?

Rozpoczynanie badania

  • Rozpocznij badanie, wykonując ślad użycia procesora CPU. Narzędzie Użycie procesora CPU jest często przydatne do rozpoczęcia badań wydajności i optymalizacji kodu w celu zmniejszenia kosztów.
  • Następnie, jeśli chcesz uzyskać dodatkowe szczegółowe informacje, aby pomóc w odizolowaniu problemów lub poprawić wydajność, biorąc pod uwagę zbieranie śladu przy użyciu jednego z innych narzędzi profilowania. Na przykład: .
    • Przyjrzyj się użyciu pamięci. W przypadku platformy .NET najpierw wypróbuj narzędzie alokacji obiektów platformy .NET. W przypadku platformy .NET lub C++możesz zapoznać się z narzędziem Użycie pamięci.
    • Jeśli aplikacja korzysta z operacji we/wy pliku, użyj narzędzia We/Wy pliku.
    • Jeśli używasz platformy ADO.NET lub Entity Framework, możesz spróbować użyć narzędzia Baza danych, aby zbadać zapytania SQL, dokładny czas zapytania i in.

Przykład zbierania danych

Przykładowe zrzuty ekranu przedstawione w tym artykule są oparte na aplikacji platformy .NET, która uruchamia zapytania względem bazy danych blogów i skojarzonych wpisów w blogu. Najpierw zbadasz ślad użycia procesora CPU, aby wyszukać możliwości optymalizacji kodu i zmniejszyć koszty obliczeń. Po zapoznaniu się z ogólnym pomysłem na to, co się dzieje, przyjrzysz się również śladom innych narzędzi profilowania, aby pomóc w odizolowaniu problemów.

Zbieranie danych wymaga następujących kroków (nie pokazano tutaj):

  • Ustawianie aplikacji na kompilację wydania
  • Wybierz narzędzie Użycie procesora CPU z profilera wydajności (Alt+F2). (Późniejsze kroki obejmują kilka innych narzędzi).
  • Z poziomu profilera wydajności uruchom aplikację i zbierz ślad.

Sprawdzanie obszarów wysokiego użycia procesora CPU

Zacznij od zebrania śladu za pomocą narzędzia Użycie procesora CPU. Po załadowaniu danych diagnostycznych najpierw sprawdź początkową stronę raportu diagsession zawierającą Szczegółowe informacje górną i ścieżkę gorącą. Ścieżka gorąca pokazuje ścieżkę kodu z najwyższym użyciem procesora CPU w aplikacji. Te sekcje mogą zawierać porady ułatwiające szybkie identyfikowanie problemów z wydajnością, które można poprawić.

Możesz również wyświetlić ścieżkę gorącą w widoku Drzewo wywołań. Aby otworzyć ten widok, użyj linku Otwórz szczegóły w raporcie, a następnie wybierz pozycję Drzewo wywołań.

W tym widoku zostanie ponownie wyświetlona ścieżka gorąca, która pokazuje wysokie użycie procesora CPU dla GetBlogTitleX metody w aplikacji przy użyciu około 60% udziału procesora CPU aplikacji. Jednak wartość Self CPU dla GetBlogTitleX parametru jest niska, tylko około 10%. W przeciwieństwie do całkowitego użycia procesora CPU wartość Self CPU nie obejmuje czasu spędzonego w innych funkcjach, więc wiemy, że przyjrzymy się dalej widokowi Drzewa wywołań dla rzeczywistego wąskiego gardła.

Zrzut ekranu przedstawiający widok Drzewa wywołań w narzędziu Użycie procesora CPU.

GetBlogTitleX Wykonuje wywołania zewnętrzne do dwóch bibliotek DLL LINQ, które korzystają z większości czasu procesora CPU, co wynika z bardzo wysokich wartości procesora CPU . Jest to pierwsza wskazówka, którą możesz chcieć wyszukać zapytanie LINQ jako obszar do optymalizacji.

Zrzut ekranu przedstawiający widok Drzewa wywołań w narzędziu Użycie procesora CPU z wyróżnionym własnym procesorem CPU.

Aby uzyskać zwizualizowane drzewo wywołań i inny widok danych, przejdź do widoku Wykres płomienia (wybierz z tej samej listy co drzewo wywołań). W tym miejscu wygląda na to, że GetBlogTitleX metoda jest odpowiedzialna za wiele użycia procesora CPU aplikacji (pokazano na żółtym rysunku). Zewnętrzne wywołania bibliotek DLL LINQ są wyświetlane pod polem GetBlogTitleX i używają całego czasu procesora CPU dla metody .

Zrzut ekranu przedstawiający widok Wykres płomienia w narzędziu Użycie procesora CPU.

Zbieranie dodatkowych danych

Często inne narzędzia mogą dostarczać dodatkowe informacje ułatwiające analizę i izolowanie problemu. Na przykład, ponieważ zidentyfikowaliśmy biblioteki DLL LINQ, najpierw spróbujemy użyć narzędzia Baza danych. Możesz wybrać to narzędzie z wieloma opcjami wraz z użyciem procesora CPU. Po zebraniu śladu wybierz kartę Zapytania na stronie diagnostyki.

Na karcie Zapytania dla śledzenia bazy danych można zobaczyć pierwszy wiersz przedstawiający najdłuższe zapytanie, 2446 ms. Kolumna Rekordy pokazuje liczbę rekordów odczytanych przez zapytanie. Te informacje można użyć do późniejszego porównania.

Zrzut ekranu przedstawiający zapytania bazy danych w narzędziu Baza danych.

Sprawdzając instrukcję SELECT wygenerowaną przez LINQ w kolumnie Query, należy zidentyfikować pierwszy wiersz jako zapytanie skojarzone z GetBlogTitleX metodą . Aby wyświetlić pełny ciąg zapytania, rozwiń szerokość kolumny, jeśli chcesz. Pełny ciąg zapytania to:

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"

Zwróć uwagę, że pobierasz tutaj wiele wartości kolumn, być może więcej niż potrzebujesz.

Aby zobaczyć, co dzieje się z aplikacją pod względem użycia pamięci, zbierz ślad przy użyciu narzędzia alokacji obiektów platformy .NET (w przypadku języka C++, zamiast tego użyj narzędzia Użycie pamięci). Widok Drzewo wywołań w śladzie pamięci pokazuje ścieżkę gorącą i pomaga zidentyfikować obszar wysokiego użycia pamięci. W tym momencie nie ma zaskoczenia, GetBlogTitleX że metoda wydaje się generować wiele obiektów! W rzeczywistości ponad 900 000 alokacji obiektów.

Zrzut ekranu przedstawiający widok Drzewa wywołań w narzędziu alokacji obiektów platformy .NET.

Większość utworzonych obiektów to ciągi, tablice obiektów i int32. Możesz zobaczyć, jak te typy są generowane, sprawdzając kod źródłowy.

Optymalizowanie kodu

Nadszedł czas, aby przyjrzeć się kodowi źródłowemu GetBlogTitleX . W narzędziu alokacji obiektów platformy .NET kliknij prawym przyciskiem myszy metodę i wybierz polecenie Przejdź do pliku źródłowego. W kodzie źródłowym dla GetBlogTitleXprogramu znajduje się następujący kod, który odczytuje bazę danych przy użyciu linQ.

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}");
      }
  }
}

Ten kod używa foreach pętli do wyszukiwania bazy danych dla wszystkich blogów z "Fred Smith" jako autor. Patrząc na to, widać, że wiele obiektów jest generowanych w pamięci: nowa tablica obiektów dla każdego bloga w bazie danych, skojarzone ciągi dla każdego adresu URL i wartości właściwości zawartych w wpisach, takich jak identyfikator blogu.

Wykonasz kilka badań i znajdziesz kilka typowych zaleceń dotyczących sposobu optymalizacji zapytań LINQ i wymyślić ten kod.

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

W tym kodzie wprowadzono kilka zmian, które pomogą zoptymalizować zapytanie:

  • Dodaj klauzulę Where i zlikwiduj foreach jedną z pętli.
  • Project tylko właściwość Title w instrukcji Select , która jest potrzebna w tym przykładzie.

Następnie przetestuj ponownie przy użyciu narzędzi profilowania.

Sprawdzanie wyników

Po zaktualizowaniu kodu uruchom ponownie narzędzie Użycie procesora CPU, aby zebrać ślad. Widok Drzewo wywołań pokazuje, że GetBlogTitleX działa tylko 1754 ms, używając 37% całkowitej sumy procesora CPU aplikacji, co znacznie poprawia się z 59%.

Zrzut ekranu przedstawiający ulepszone użycie procesora CPU w widoku Drzewa wywołań narzędzia Użycie procesora CPU.

Przejdź do widoku Wykres płomienia , aby wyświetlić kolejną wizualizację ulepszeń. W tym widoku GetBlogTitleX jest również używana mniejsza część procesora CPU.

Zrzut ekranu przedstawiający ulepszone użycie procesora CPU w widoku Wykres płomienia narzędzia Użycie procesora CPU.

Sprawdź wyniki w śladzie narzędzia Baza danych, a tylko dwa rekordy są odczytywane przy użyciu tego zapytania, a nie 100 000! Ponadto zapytanie jest znacznie uproszczone i eliminuje niepotrzebny element LEFT JOIN, który został wygenerowany wcześniej.

Zrzut ekranu przedstawiający krótszy czas wykonywania zapytań w narzędziu Baza danych.

Następnie ponownie sprawdź wyniki w narzędziu alokacji obiektów platformy .NET i zobacz, że GetBlogTitleX jest to odpowiedzialne tylko za 56 000 alokacji obiektów, prawie 95% redukcji z 900 000!

Zrzut ekranu przedstawiający ograniczone alokacje pamięci w narzędziu alokacji obiektów platformy .NET.

Powtarzanie

Może być konieczne wiele optymalizacji i nadal można iterować ze zmianami kodu, aby zobaczyć, które zmiany zwiększają wydajność i zmniejszają koszt obliczeń.

Następne kroki

Poniższe wpisy w blogu zawierają więcej informacji, aby ułatwić efektywne korzystanie z narzędzi do wydajności programu Visual Studio.