Casestudy: Beginnershandleiding voor het optimaliseren van code en het verlagen van de rekenkosten (C#, Visual Basic, C++, F#)
Het verminderen van uw rekentijd betekent dat u kosten verlaagt, zodat het optimaliseren van uw code geld kan besparen. In deze casestudy wordt een voorbeeldtoepassing met prestatieproblemen gebruikt om te laten zien hoe u profileringshulpprogramma's gebruikt om de efficiëntie te verbeteren. Als u profileringsprogramma's wilt vergelijken, raadpleegt u Welk hulpprogramma moet ik kiezen?
In deze casestudy worden de volgende onderwerpen behandeld:
- Het belang van codeoptimalisatie en de impact ervan op het verlagen van de rekenkosten.
- Visual Studio-profileringshulpprogramma's gebruiken om de prestaties van toepassingen te analyseren.
- Hoe u de gegevens van deze hulpprogramma's interpreteert om prestatieknelpunten te identificeren.
- Praktische strategieën toepassen om code te optimaliseren, gericht op CPU-gebruik, geheugentoewijzing en database-interacties.
Volg de stappen en pas deze technieken vervolgens toe op uw eigen toepassingen om ze efficiënter en rendabeler te maken.
Optimalisatiestudie
De voorbeeldtoepassing die in deze casestudy wordt onderzocht, is een .NET-toepassing die query's uitvoert op een database met blogs en blogberichten. Het maakt gebruik van Entity Framework, een populaire ORM (Object-Relational-toewijzing) voor .NET, om te communiceren met een lokale SQLite-database. De toepassing is gestructureerd voor het uitvoeren van een groot aantal query's, waarbij een praktijkscenario wordt gesimsimeerd waarbij een .NET-toepassing mogelijk vereist is voor het afhandelen van uitgebreide taken voor het ophalen van gegevens. De voorbeeldtoepassing is een aangepaste versie van de Entity Framework beginnersgids.
Het primaire prestatieprobleem met de voorbeeldtoepassing ligt in de manier waarop het rekenresources beheert en communiceert met de database. De toepassing heeft een prestatieknelpunt dat de efficiëntie aanzienlijk beïnvloedt en daarom de rekenkosten die zijn gekoppeld aan het uitvoeren ervan. Het probleem omvat de volgende symptomen:
hoog CPU-gebruik: toepassingen kunnen inefficiënte berekeningen of verwerkingstaken uitvoeren op een manier die onnodig een grote hoeveelheid CPU-resources verbruikt. Dit kan leiden tot trage reactietijden en hogere operationele kosten.
inefficiënte geheugentoewijzing: toepassingen kunnen soms problemen ondervinden met betrekking tot geheugengebruik en toewijzing. In .NET-apps kan inefficiënt geheugenbeheer leiden tot een grotere garbagecollection, wat op zijn beurt van invloed kan zijn op de prestaties van toepassingen.
Overhead van databaseinteractie: toepassingen die een groot aantal query's uitvoeren op een database kunnen knelpunten ondervinden met betrekking tot database-interacties. Dit omvat inefficiënte query's, overmatige databaseaanroepen en slecht gebruik van Entity Framework-mogelijkheden, die allemaal de prestaties kunnen verminderen.
De casestudy is erop gericht deze problemen op te lossen door de profileringshulpprogramma's van Visual Studio te gebruiken om de prestaties van de toepassing te analyseren. Door inzicht te krijgen in waar en hoe de prestaties van de toepassing kunnen worden verbeterd, kunnen ontwikkelaars optimalisaties implementeren om het CPU-gebruik te verminderen, de efficiëntie van geheugentoewijzing te verbeteren, database-interacties te stroomlijnen en resourcegebruik te optimaliseren. Het uiteindelijke doel is om de algehele prestaties van de toepassing te verbeteren, waardoor deze efficiënter en rendabeler kan worden uitgevoerd.
Uitdaging
Het oplossen van de prestatieproblemen in de .NET-voorbeeldtoepassing biedt verschillende uitdagingen. Deze uitdagingen komen voort uit de complexiteit van het diagnosticeren van prestatieknelpunten. De belangrijkste uitdagingen bij het oplossen van de beschreven problemen zijn als volgt:
prestatieknelpunten vaststellen: een van de belangrijkste uitdagingen is het nauwkeurig identificeren van de hoofdoorzaken van de prestatieproblemen. Hoog CPU-gebruik, inefficiënte geheugentoewijzing en overhead voor databaseinteractie kunnen meerdere factoren hebben. Ontwikkelaars moeten profileringshulpprogramma's effectief gebruiken om deze problemen vast te stellen. Hiervoor is enige kennis nodig van hoe deze hulpprogramma's werken en hoe ze hun uitvoer kunnen interpreteren.
kennis- en resourcebeperkingen: ten slotte kunnen teams beperkingen ondervinden met betrekking tot kennis, expertise en resources. Profileren en optimaliseren van een toepassing vereist specifieke vaardigheden en ervaring, en niet alle teams hebben mogelijk onmiddellijk toegang tot deze resources.
Voor het aanpakken van deze uitdagingen is een strategische aanpak vereist die effectief gebruik combineert van profileringstools, technische kennis en zorgvuldige planning en testen. De casestudy is bedoeld om ontwikkelaars door dit proces te begeleiden, strategieën en inzichten te bieden om deze uitdagingen te overwinnen en de prestaties van de toepassing te verbeteren.
Strategie
Hier volgt een algemeen overzicht van de aanpak in deze casestudy:
- We beginnen met het onderzoek door een tracering van het CPU-gebruik te maken. Het hulpprogramma voor CPU-gebruik van Visual Studio is vaak handig om prestatieonderzoeken te starten en code te optimaliseren om de kosten te verlagen.
- Om vervolgens aanvullende inzichten te krijgen om problemen te isoleren of de prestaties te verbeteren, verzamelen we een tracering met behulp van een van de andere profileringshulpprogramma's. Bijvoorbeeld:
- We bekijken het geheugengebruik. Voor .NET proberen we eerst het hulpprogramma .NET-objecttoewijzing. (Voor .NET of C++ kunt u in plaats daarvan het hulpprogramma Geheugengebruik bekijken.)
- Voor ADO.NET of Entity Framework kunnen we het hulpprogramma Database gebruiken om SQL-query's, precieze querytijd en meer te onderzoeken.
Voor het verzamelen van gegevens zijn de volgende taken vereist:
- De app instellen op een release-build.
- Het hulpprogramma CPU-gebruik selecteren in de Performance Profiler (Alt+F2). (Latere stappen omvatten enkele van de andere hulpprogramma's.)
- Start de app met de Performance Profiler en verzamel een trace.
Gebieden met hoog CPU-gebruik controleren
Na het verzamelen van een trace met het hulpprogramma CPU-gebruik en het laden ervan in Visual Studio, controleren we eerst de .diagsession rapportpagina met samengevatte gegevens. Gebruik de koppeling Open details in het rapport.
In de detailweergave van het rapport, open de weergave Oproepstructuur. Het codepad met het hoogste CPU-gebruik in de app wordt het hot pathgenoemd. Het pictogram van het hot path-vlam () kan helpen om snel prestatieproblemen te identificeren die mogelijk kunnen worden verbeterd.
In de weergave Oproepstructuur ziet u een hoog CPU-gebruik voor de methode GetBlogTitleX
in de app, met een aandeel van ongeveer 60% van het CPU-gebruik van de app. De zelf-CPU- waarde voor GetBlogTitleX
is echter laag, slechts ongeveer .10%. In tegenstelling tot de totale CPU , sluit de zelf-CPU-waarde tijd uit die in andere functies is besteed, dus we weten dat we dieper in de oproepstructuur moeten zoeken naar het echte knelpunt.
GetBlogTitleX
maakt externe aanroepen naar twee LINQ-DLL's, die de meeste CPU-tijd gebruiken, zoals wordt aangetoond door de zeer hoge self-CPU- waarden. Dit is de eerste aanwijzing dat een LINQ-query mogelijk een gebied is om te optimaliseren.
Als u een gevisualiseerde oproepstructuur en een andere weergave van de gegevens wilt krijgen, opent u de Flame Graph weergave. (Of klik met de rechtermuisknop op GetBlogTitleX
en kies weergave in Flame Graph.) Hier ziet het er opnieuw uit dat de GetBlogTitleX
methode verantwoordelijk is voor een groot deel van het CPU-gebruik van de app (geel). Externe aanroepen naar de LINQ-DLL's worden weergegeven onder het vak GetBlogTitleX
en ze gebruiken alle CPU-tijd voor de methode.
Aanvullende gegevens verzamelen
Vaak kunnen andere hulpprogramma's aanvullende informatie bieden om de analyse te helpen en het probleem te isoleren. In deze casestudy gaan we als volgt te werk:
- Bekijk eerst het geheugengebruik. Er kan een correlatie zijn tussen een hoog CPU-gebruik en een hoog geheugengebruik, zodat het handig kan zijn om beide te bekijken om het probleem te isoleren.
- Omdat we de LINQ-DLL's hebben geïdentificeerd, kijken we ook naar het hulpprogramma Database.
Het geheugengebruik controleren
Om te zien wat er met de app gebeurt in termen van geheugengebruik, verzamelen we een tracering met behulp van het hulpprogramma .NET-objecttoewijzing (voor C++kunt u in plaats daarvan het hulpprogramma Geheugengebruik gebruiken). De oproepstructuur weergave in de geheugentracering toont het hot path en helpt ons bij het identificeren van een gebied met een hoog geheugengebruik. Geen verrassing op dit moment, de GetBlogTitleX
methode lijkt veel objecten te genereren! Meer dan 900.000 objecttoewijzingen, om precies te zijn.
De meeste gemaakte objecten zijn tekenreeksen, objectmatrices en Int32s. We kunnen mogelijk zien hoe deze typen worden gegenereerd door de broncode te onderzoeken.
Controleer de query in het hulpprogramma Database
In de Performance Profiler selecteren we het hulpprogramma Database in plaats van CPU-gebruik (of selecteer beide). Wanneer we een trace hebben verzameld, opent u het tabblad Queries op de diagnostische pagina. Op het tabblad Query's voor de databasetracering ziet u de eerste rij met de langste query, 2446 ms. In de kolom Records ziet u hoeveel records de query leest. U kunt deze informatie gebruiken voor latere vergelijking.
Door de SELECT
-instructie te onderzoeken die is gegenereerd door LINQ in de kolom Query, identificeren we de eerste rij als de query die is gekoppeld aan de GetBlogTitleX
methode. Als u de volledige querytekenreeks wilt weergeven, vouwt u de kolombreedte uit. De volledige querytekenreeks is:
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"
U ziet dat de app hier veel kolomwaarden opzoekt, misschien meer dan we nodig hebben. Laten we eens kijken naar de broncode.
Code optimaliseren
Het is tijd om de GetBlogTitleX
broncode te bekijken. Klik in het hulpprogramma Database met de rechtermuisknop op de query en kies Ga naar bronbestand. In de broncode voor GetBlogTitleX
vinden we de volgende code die LINQ gebruikt om de database te lezen.
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}");
}
}
}
Deze code maakt gebruik van foreach
lussen om in de database te zoeken naar blogs met 'Fred Smith' als auteur. U kunt zien dat er veel objecten worden gegenereerd in het geheugen: een nieuwe objectmatrix voor elke blog in de database, gekoppelde tekenreeksen voor elke URL en waarden voor eigenschappen in de berichten, zoals blog-id.
We doen wat onderzoek en vinden enkele algemene aanbevelingen voor het optimaliseren van LINQ-query's. We kunnen ook tijd besparen en Copilot het onderzoek voor ons laten doen.
Als we Copilot gebruiken, selecteren we Vraag Copilot in het contextmenu en typen we de volgende vraag:
Can you make the LINQ query in this method faster?
Tip
U kunt slash-opdrachten zoals /optimize gebruiken om goede vragen voor Copilot te vormen.
In dit voorbeeld geeft Copilot de volgende voorgestelde codewijzigingen, samen met een uitleg.
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}");
}
}
Deze code bevat verschillende wijzigingen om de query te optimaliseren:
- De
Where
-clausule toegevoegd en een van deforeach
-lussen verwijderd. - Projecteerde alleen de eigenschap Titel in de instructie
Select
. Dit is alles wat we in dit voorbeeld nodig hebben.
Vervolgens testen we opnieuw met behulp van de profileringshulpprogramma's.
Resultaten
Na het bijwerken van de code voeren we het hulpprogramma CPU-gebruik opnieuw uit om een tracering te verzamelen. De oproepstructuur-weergave laat zien dat GetBlogTitleX
slechts 1754 ms draait, waarbij 37% van het totale CPU-gebruik van de app wordt gebruikt, een aanzienlijke verbetering ten opzichte van 59%.
Schakel over naar de Flame Graph weergave om een andere visualisatie weer te geven met de verbetering. In deze weergave gebruikt GetBlogTitleX
ook een kleiner deel van de CPU.
Controleer de resultaten in de databasetooltracering en slechts twee records worden gelezen met behulp van deze query, in plaats van 100.000! De query is ook veel vereenvoudigd en elimineert de onnodige LEFT JOIN die eerder is gegenereerd.
Vervolgens controleren we de resultaten opnieuw in het hulpprogramma .NET-objecttoewijzing en zien we dat GetBlogTitleX
alleen verantwoordelijk is voor 56.000 objecttoewijzingen, bijna een vermindering van 95% van 900.000!
Itereren
Mogelijk zijn er meerdere optimalisaties nodig en kunnen we doorgaan met codewijzigingen om te zien welke wijzigingen de prestaties verbeteren en de rekenkosten verlagen.
Volgende stappen
De volgende artikelen en blogposts bieden meer informatie om u te helpen de Visual Studio-prestatiehulpprogramma's effectief te gebruiken.
- Case-study: Een prestatieprobleem isoleren
- Case Study: Dubbele Prestatie in minder dan 30 minuten
- Visual Studio-prestaties verbeteren met het nieuwe instrumentatieprogramma