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:
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ě.
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 () 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.
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.
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.
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ů.
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í.
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 GetBlogTitleX
najdeme 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é zeforeach
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 %.
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.
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.
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!
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.
- Případová studie: Izolace problému s výkonem
- Případová studie: Dvojitý výkon za méně než 30 minut
- Vylepšení výkonu sady Visual Studio pomocí nového nástroje instrumentace