Sdílet prostřednictvím


Případová studie: Průvodce začátečníkem pro optimalizaci kódu a snížení nákladů na výpočetní prostředky (C#, Visual Basic, C++, F#)

Snížení výpočetní doby znamená snížení nákladů, takže optimalizace kódu může ušetřit peníze. Tato případová studie používá ukázkovou aplikaci s problémy s výkonem, aby ukázala, jak pomocí nástrojů pro profilaci zlepšit efektivitu. Pokud chcete porovnat nástroje pro profilaci, podívejte se, který nástroj mám zvolit?

Tato případová studie se zabývá těmito tématy:

  • Důležitost optimalizace kódu a jejího dopadu na snížení nákladů na výpočetní prostředky.
  • Použití nástrojů pro profilaci sady Visual Studio k analýze výkonu aplikace
  • Jak interpretovat data poskytovaná těmito nástroji za účelem identifikace kritických bodů výkonu.
  • Jak použít praktické strategie pro optimalizaci kódu, zaměření na využití procesoru, přidělení paměti a interakce databáze.

Postupujte podle pokynů a pak tyto techniky použijte u vlastních aplikací, aby byly efektivnější a nákladově efektivnější.

Případová studie optimalizace

Ukázková aplikace vyšetřená v této případové studii je aplikace .NET, která spouští dotazy na databázi blogů a blogových příspěvků. K interakci s místní databází SQLite využívá Entity Framework, oblíbené mapování ORM (Object-Relational Mapping) pro .NET. Aplikace je strukturovaná tak, aby prováděla velký počet dotazů a simuluje skutečný scénář, ve kterém může být aplikace .NET nutná ke zpracování rozsáhlých úloh načítání dat. Ukázková aplikace je upravená verze ukázky Začínáme s Entity Frameworkem.

Primární problém s výkonem ukázkové aplikace spočívá v tom, jak spravuje výpočetní prostředky a komunikuje s databází. Aplikace má kritický bod výkonu, který výrazně ovlivňuje jeho efektivitu a v důsledku toho náklady na výpočetní prostředky spojené se spuštěním. Problém zahrnuje následující příznaky:

  • Vysoké využití procesoru: Aplikace můžou provádět neefektivní výpočty nebo úlohy zpracování způsobem, který zbytečně spotřebovává velké množství prostředků procesoru. To může vést k pomalé době odezvy a zvýšení provozních nákladů.

  • Neefektivní přidělení paměti: Aplikace můžou někdy čelit problémům souvisejícím s využitím a přidělením paměti. V aplikacích .NET může neefektivní správa paměti vést ke zvýšení uvolňování paměti, což může zase ovlivnit výkon aplikace.

  • Režie při interakci databáze: U aplikací, které spouštějí velký počet dotazů na databázi, mohou docházet k kritickým bodům souvisejícím s interakcemi databáze. To zahrnuje neefektivní dotazy, nadměrné volání databáze a špatné využití funkcí Entity Framework, z nichž všechny můžou snížit výkon.

Případová studie se zaměřuje na řešení těchto problémů pomocí nástrojů pro profilaci sady Visual Studio k analýze výkonu aplikace. Díky pochopení toho, kde a jak je možné zlepšit výkon aplikace, můžou vývojáři implementovat optimalizace, které snižují využití procesoru, zlepšují efektivitu přidělování paměti, zjednodušují interakce s databázemi a optimalizují využití prostředků. Konečným cílem je zvýšit celkový výkon aplikace, aby byla efektivnější a nákladově efektivnější.

Úkol

Řešení problémů s výkonem v ukázkové aplikaci .NET představuje několik problémů. Tyto problémy vyplývají ze složitosti diagnostiky kritických bodů výkonu. Hlavní výzvy při řešení popsaných problémů jsou následující:

  • Diagnostika kritických bodů výkonu: Jedním z hlavních problémů je přesná identifikace hlavních příčin problémů s výkonem. Vysoké využití procesoru, neefektivní přidělení paměti a režijní náklady na interakci s databází můžou mít několik faktorů, které přispívají. Vývojáři musí k diagnostice těchto problémů efektivně používat nástroje pro profilaci, které vyžadují určité znalosti o tom, jak tyto nástroje fungují a jak interpretovat jejich výstup.

  • Omezení znalostí a zdrojů: Nakonec mohou týmy čelit omezením souvisejícím se znalostmi, odbornými znalostmi a zdroji. Profilace a optimalizace aplikace vyžaduje specifické dovednosti a zkušenosti, a ne všechny týmy mohou mít okamžitý přístup k těmto prostředkům.

Řešení těchto výzev vyžaduje strategický přístup, který kombinuje efektivní použití nástrojů profilace, technických znalostí a pečlivého plánování a testování. Případová studie má za cíl vést vývojáře tímto procesem, poskytovat strategie a přehledy k překonání těchto problémů a zlepšit výkon aplikace.

Strategie

Tady je základní pohled na přístup v této případové studii:

  • Šetření zahájíme tím, že provedeme trasování využití procesoru. Nástroj Využití procesoru sady Visual Studio je často užitečný k zahájení šetření výkonu a optimalizaci kódu pro snížení nákladů.
  • V dalším kroku získáme další přehledy, které vám pomůžou izolovat problémy nebo zlepšit výkon, shromáždíme trasování pomocí některého z dalších nástrojů pro profilaci. Příklad:
    • Podíváme se na využití paměti. Pro .NET nejprve vyzkoušíme nástroj pro přidělování objektů .NET. (V případě .NET nebo C++ se můžete podívat na nástroj Využití paměti.)
    • Pro ADO.NET nebo Entity Framework můžeme pomocí databázového nástroje zkoumat dotazy SQL, přesný čas dotazu a další.

Shromažďování dat vyžaduje následující úlohy:

  • Nastavení aplikace na build vydané verze
  • Výběr nástroje Využití procesoru z profileru výkonu (Alt+F2) (Další kroky zahrnují několik dalších nástrojů.)
  • V profileru výkonu spusťte aplikaci a shromážděte trasování.

Kontrola oblastí vysokého využití procesoru

Po shromáždění trasování pomocí nástroje Využití procesoru a jeho načtení do sady Visual Studio nejprve zkontrolujeme úvodní stránku sestavy .diagsession , která zobrazuje souhrnná data. Použijte odkaz Otevřít podrobnosti v sestavě.

Snímek obrazovky s otevřením podrobností v nástroji Využití procesoru

V zobrazení podrobností sestavy otevřete zobrazení Stromu volání. Cesta kódu s nejvyšším využitím procesoru v aplikaci se nazývá horká cesta. Ikona plamene horké cesty (Snímek obrazovky s ikonou Horká cesta) může pomoct rychle identifikovat problémy s výkonem, které by mohly být vylepšeny.

V zobrazení Stromu volání můžete vidět vysoké využití procesoru pro GetBlogTitleX metodu v aplikaci pomocí přibližně 60% podílu využití procesoru aplikace. Hodnota GetBlogTitleX vlastního procesoru je ale nízká, pouze přibližně 0,10 %. Na rozdíl od celkového procesoru vyloučí hodnota vlastního procesoru čas strávený v jiných funkcích, takže víme, že se podíváme dál dolů ve stromu volání pro skutečný kritický bod.

Snímek obrazovky se stromem volání v nástroji Využití procesoru

GetBlogTitleX provádí externí volání dvou knihoven LINQ DLL, které využívají většinu času procesoru, což je důkazem velmi vysokých hodnot self cpu . Toto je první vodítko, že dotaz LINQ může být oblastí pro optimalizaci.

Snímek obrazovky se zobrazením Stromu volání v nástroji Využití procesoru se zvýrazněným vlastním procesorem

Pokud chcete získat vizualizovaný strom volání a jiné zobrazení dat, otevřete zobrazení Flame Graph . (Nebo klikněte pravým tlačítkem myši GetBlogTitleX a zvolte Zobrazit v plamenovém grafu.) Opět to vypadá, že GetBlogTitleX metoda odpovídá za velké množství využití procesoru aplikace (znázorněno žlutě). Externí volání knihoven DLL LINQ se zobrazí pod GetBlogTitleX polem a používají pro metodu veškerý čas procesoru.

Snímek obrazovky se zobrazením Flame Graph v nástroji Využití procesoru

Shromažďování dalších dat

Další nástroje můžou často poskytovat další informace, které vám pomůžou s analýzou a izolovat problém. V této případové studii používáme následující přístup:

  • Nejprve se podívejte na využití paměti. Může existovat korelace mezi vysokým využitím procesoru a vysokým využitím paměti, takže může být užitečné se podívat na obojí, abyste problém izolovali.
  • Vzhledem k tomu, že jsme identifikovali knihovny DLL LINQ, podíváme se také na nástroj Databáze.

Kontrola využití paměti

Abychom zjistili, co se s aplikací děje z hlediska využití paměti, shromažďujeme trasování pomocí nástroje pro přidělování objektů .NET (pro C++, místo toho můžete použít nástroj Využití paměti). Zobrazení Stromu volání v trasování paměti ukazuje horkou cestu a pomáhá nám identifikovat oblast vysokého využití paměti. V tomto okamžiku GetBlogTitleX se zdá, že metoda generuje velké množství objektů! Ve skutečnosti bylo přiděleno více než 900 000 objektů.

Snímek obrazovky se zobrazením stromu volání v nástroji pro přidělování objektů .NET

Většina vytvořených objektů je řetězce, pole objektů a int32. Možná uvidíme, jak se tyto typy generují prozkoumáním zdrojového kódu.

Kontrola dotazu v nástroji Databáze

V profileru výkonu vybereme nástroj Databáze místo využití procesoru (nebo obojí). Když jsme shromáždili trasování, otevřete na stránce diagnostiky kartu Dotazy. Na kartě Dotazy pro trasování databáze uvidíte první řádek s nejdelším dotazem 2446 ms. Sloupec Záznamy ukazuje, kolik záznamů dotaz načte. Tyto informace můžete použít k pozdějšímu porovnání.

Snímek obrazovky s databázovými dotazy v nástroji Database

Prozkoumáním SELECT příkazu vygenerovaného LINQ ve sloupci Query identifikujeme první řádek jako dotaz přidružený k GetBlogTitleX metodě. Pokud chcete zobrazit celý řetězec dotazu, rozbalte šířku sloupce. Úplný řetězec dotazu je:

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"

Všimněte si, že aplikace tady načítá hodně hodnot sloupců, možná více, než potřebujeme. Pojďme se podívat na zdrojový kód.

Optimalizace kódu

Je čas se podívat na GetBlogTitleX zdrojový kód. V nástroji Databáze klikněte pravým tlačítkem myši na dotaz a zvolte Přejít na zdrojový soubor. Ve zdrojovém kódu pro GetBlogTitleXnajdeme následující kód, který ke čtení databáze používá 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}");
      }
  }
}

Tento kód používá foreach smyčky k vyhledávání v databázi pro všechny blogy s "Fred Smith" jako autor. Když se na to podíváte, můžete vidět, že se v paměti generuje hodně objektů: nové pole objektů pro každý blog v databázi, přidružené řetězce pro každou adresu URL a hodnoty vlastností obsažených v příspěvcích, jako je ID blogu.

Provedeme trochu průzkumu a najdeme několik běžných doporučení pro optimalizaci dotazů LINQ. Alternativně můžeme ušetřit čas a nechat Copilot udělat výzkum za nás.

Pokud používáme Copilot, v místní nabídce vybereme možnost Zeptat se zkopírovaného objektu a zadáme následující otázku:

Can you make the LINQ query in this method faster?

Tip

Příkazy lomítka, jako je /optimize , můžete použít k vytvoření vhodných otázek pro Copilot.

V tomto příkladu poskytuje Copilot následující navrhované změny kódu spolu s vysvětlením.

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

Tento kód obsahuje několik změn, které vám pomůžou optimalizovat dotaz:

  • Where Přidání klauzule a odstranění jedné ze foreach smyček
  • Projected only the Title property in the Select statement, which is all we need in this example.

V dalším kroku znovu otestujeme pomocí nástrojů pro profilaci.

Výsledky

Po aktualizaci kódu znovu spustíme nástroj Využití procesoru a shromáždíme trasování. Zobrazení Stromu volání ukazuje, že GetBlogTitleX je spuštěno pouze 1754 ms, s využitím 37 % celkového využití procesoru aplikace, významné zlepšení z 59 %.

Snímek obrazovky s vylepšeným využitím procesoru v zobrazení stromu volání nástroje Využití procesoru

Přepněte do zobrazení Flame Graph a zobrazte další vizualizaci zobrazující vylepšení. V tomto zobrazení GetBlogTitleX také používá menší část procesoru.

Snímek obrazovky s vylepšeným využitím procesoru v zobrazení Flame Graph nástroje Využití procesoru

Zkontrolujte výsledky v trasování databázového nástroje a pomocí tohoto dotazu se čtou jenom dva záznamy místo 100 000! Dotaz je také hodně zjednodušený a eliminuje nepotřebné funkce LEFT JOIN, která byla vygenerována dříve.

Snímek obrazovky s rychlejším časem dotazu v nástroji Databáze

V dalším kroku znovu zkontrolujeme výsledky v nástroji pro přidělování objektů .NET a zjistíme, že GetBlogTitleX je zodpovědný pouze za 56 000 přidělení objektů, téměř 95% snížení z 900 000!

Snímek obrazovky s omezenými přiděleními paměti v nástroji pro přidělování objektů .NET

Iterovat

Může být nutné provést několik optimalizací a můžeme pokračovat iterací se změnami kódu, abychom zjistili, které změny zlepšují výkon a pomáhají snížit náklady na výpočetní prostředky.

Další kroky

Následující články a blogové příspěvky obsahují další informace, které vám pomůžou efektivně používat nástroje pro výkon sady Visual Studio.