Megosztás a következőn keresztül:


Esettanulmány: Kezdő útmutató a kód optimalizálásához és a számítási költségek csökkentéséhez (C#, Visual Basic, C++, F#)

A kód optimalizálása csökkenti a számítási időt és a költségeket. Ez az esettanulmány bemutatja, hogyan használható a Visual Studio profilkészítési eszközeivel a minta .NET-alkalmazások teljesítményproblémáinak azonosítására és megoldására. Ha összehasonlítja a profilkészítési eszközöket, olvassa el Melyik eszközt válasszam?

Ez az útmutató a következőket ismerteti:

  • A Visual Studio profilkészítési eszközeinek használata a teljesítmény elemzéséhez és javításához.
  • Gyakorlati stratégiák a processzorhasználat, a memóriafoglalás és az adatbázis-interakciók optimalizálásához.

Ezeket a technikákat alkalmazva hatékonyabbá teheti saját alkalmazásait.

Optimalizálási esettanulmány

A minta .NET-alkalmazás lekérdezéseket futtat a blogokat és bejegyzéseket tartalmazó SQLite-adatbázison az Entity Framework használatával. Számos lekérdezést hajt végre, valós adatlekérési forgatókönyvet szimulálva. Az alkalmazás az Entity Framework első lépések mintáján alapul, de egy nagyobb adatkészletet használ.

A fő teljesítményproblémák a következők:

  • Magas processzorhasználat: A nem hatékony számítások vagy feldolgozási feladatok növelik a processzorhasználatot és a költségeket.
  • Nem hatékony memóriafoglalás: A rossz memóriakezelés túlzott szemétgyűjtéshez és a teljesítmény csökkenéséhez vezet.
  • Adatbázis-többletterhelések: A nem hatékony lekérdezések és a túlzott adatbázis-hívások rontják a teljesítményt.

Ez az esettanulmány a Visual Studio profilkészítési eszközeivel rögzíti és kezeli ezeket a problémákat, így hatékonyabbá és költséghatékonyabbá szeretné tenni az alkalmazást.

Kihívás

Ezeknek a teljesítményproblémáknak a kijavítása több kihívást is magában foglal:

  • Szűk keresztmetszetek diagnosztizálása: A magas CPU-, memória- vagy adatbázisterhelés kiváltó okainak azonosítása a profilkészítési eszközök hatékony használatát és az eredmények helyes értelmezését igényli.
  • Tudás- és erőforráskorlátok: A profilkészítés és az optimalizálás speciális készségeket és tapasztalatokat igényel, amelyek nem mindig érhetők el.

A profilkészítési eszközöket, a műszaki ismereteket és a gondos tesztelést kombináló stratégiai megközelítés elengedhetetlen a kihívások leküzdéséhez.

Stratégia

Ebben az esettanulmányban a megközelítés magas szintű nézete látható:

  • Kezdje a CPU-használat nyomkövetésével a Visual Studio CPU-használati eszközével. A Visual Studio CPU-használati eszköze jó kiindulópont a teljesítményvizsgálatokhoz.
  • További nyomkövetések gyűjtése a memória- és adatbázis-elemzéshez:

Az adatgyűjtéshez a következő feladatok szükségesek:

  • Állítsa be az alkalmazást release buildre.
  • Válassza ki a CPU-használat eszközt a Performance Profilerben (Alt+F2).
  • A Performance Profilerben indítsa el az alkalmazást, és gyűjtsön egy nyomkövetést.

Magas processzorhasználatú területek vizsgálata

Miután összegyűjtöttünk egy nyomvonalat a CPU kihasználtsági eszközzel, és betöltöttük a Visual Studióba, először áttekintjük a kezdeti .diagsession riportoldalt, amely összegzett adatokat jelenít meg. Használja a Részletek megnyitása hivatkozást a jelentésben.

Képernyőkép a processzorhasználati eszköz részleteinek megnyitásáról.

A jelentés részletei nézetben nyissa meg a Hívásfa nézetet. Az alkalmazás legnagyobb processzorhasználattal rendelkező kódútvonalát gyakori elérési útnak nevezzük. A gyakori elérésű útvonal láng ikonja (Képernyőkép, amelyen a Gyakori elérésű útvonal ikon látható.) segítségével gyorsan azonosíthatók a javítható teljesítményproblémák.

A Hívásfa nézetben az alkalmazás GetBlogTitleX metódusának magas processzorhasználata látható, amely az alkalmazás processzorhasználatának körülbelül 60% részét használja. A GetBlogTitleX értéke azonban alacsony, csak körülbelül .10%. Ellentétben a teljes CPUértékével, a saját CPU érték kizárja a más függvényekben töltött időt, így távolabb kereshetjük a hívásfában a valódi szűk keresztmetszetet.

A Hívásfa nézet képernyőképe a CPU-használati eszközben.

GetBlogTitleX külső hívásokat kezdeményez két LINQ-DLL-hez, amelyek a processzoridő nagy részét használják, amint azt a nagyon magas saját PROCESSZOR értékek is bizonyítják. Ez az első jel arra, hogy a LINQ-lekérdezések optimalizálandó területnek bizonyulhatnak.

Képernyőkép a Hívásfa nézetről a CPU-használat eszközben, kiemelve a saját CPU-t.

A vizualizált hívásfa és az adatok eltérő nézetének lekéréséhez nyissa meg a Flame Graph nézetet. (Vagy kattintson a jobb gombbal a GetBlogTitleX, és válassza Nézet a Flame Graph.) Itt is úgy tűnik, hogy a GetBlogTitleX metódus felelős az alkalmazás processzorhasználatának nagy részének (sárga színnel jelenik meg). A LINQ-DLL-ek külső hívásai a GetBlogTitleX mező alatt jelennek meg, és a metódushoz szükséges processzoridőt használják.

Képernyőkép a Flame Graph nézetről a CPU-használat eszközben.

További adatok gyűjtése

Gyakran más eszközök is nyújtanak további információkat az elemzéshez és a probléma elkülönítéséhez. Ebben az esettanulmányban a következő megközelítést alkalmazzuk:

  • Először is tekintse meg a memóriahasználatot. Lehet, hogy összefüggés van a magas processzorhasználat és a magas memóriahasználat között, ezért hasznos lehet mindkettőt megvizsgálni a probléma elkülönítése érdekében.
  • Mivel azonosítottuk a LINQ DLL-eket, az Adatbázis eszközt is megvizsgáljuk.

A memóriahasználat ellenőrzése

Ha meg szeretné tekinteni, hogy mi történik az alkalmazással a memóriahasználat szempontjából, a .NET objektumfoglalási eszközzel gyűjtünk egy nyomkövetést (A C++-hoz használhatja inkább a Memóriahasználat eszközt). A Hívásfa nézet a memóriakövetésben a gyakori elérési utat mutatja, és segít azonosítani a magas memóriahasználatot. Ezen a ponton nem meglepő, hogy a GetBlogTitleX metódus úgy tűnik, hogy sok objektumot hoz létre! Valójában több mint 900 000 objektumfoglalás.

A .NET objektumfoglalási eszköz Hívásfa nézetének képernyőképe.

A létrehozott objektumok többsége karakterlánc, objektumtömb és Int32. Előfordulhat, hogy a forráskód vizsgálatával láthatjuk, hogyan jönnek létre ezek a típusok.

A lekérdezés ellenőrzése az Adatbázis eszközben

A Teljesítményprofilozóban a processzorhasználat helyett az Adatbázis eszközt választjuk (vagy mindkettőt). Miután összegyűjtöttünk egy nyomkövetést, nyissa meg a Lekérdezések lapot a diagnosztikai lapon. Az Adatbázis-nyomkövetés Lekérdezések lapján láthatja, hogy az első sorban a leghosszabb lekérdezés látható, 2446 ms. A Rekordok oszlopban látható, hogy hány rekordot olvas be a lekérdezés. Ezeket az információkat későbbi összehasonlításhoz használhatja.

Az Adatbázis eszköz adatbázis-lekérdezéseinek képernyőképe.

A LINQ által a Lekérdezés oszlopban létrehozott SELECT utasítás vizsgálatával azonosítjuk az első sort a GetBlogTitleX metódushoz társított lekérdezésként. A teljes lekérdezési sztring megtekintéséhez bontsa ki az oszlop szélességét. A teljes lekérdezési sztring a következő:

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"

Figyelje meg, hogy az alkalmazás sok oszlopértéket kér le itt, talán többet, mint amennyire szükségünk van. Nézzük meg a forráskódot.

Kód optimalizálása

Ideje áttekinteni a GetBlogTitleX forráskódot. Az Adatbázis eszközben kattintson a jobb gombbal a lekérdezésre, és válassza a Ugrás a forrásfájlralehetőséget. A GetBlogTitleXforráskódjában az alábbi kód található, amely linq használatával olvassa be az adatbázist.

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

Ez a kód foreach ciklusokkal keres az adatbázisban olyan blogokat, amelynek szerzője Fred Smith. Ha megnézzük, láthatjuk, hogy sok objektum jön létre a memóriában: egy új objektumtömb az adatbázis minden blogja számára, az egyes URL-címekhez társított sztringek és a bejegyzésekben található tulajdonságok értékei, például a blogazonosító.

Egy kis kutatást végezünk, és találunk néhány gyakori javaslatot a LINQ-lekérdezések optimalizálásához. Másik lehetőségként időt takaríthatunk meg, és hagyhatjuk, hogy Copilot végezze el a kutatást.

Ha a Copilotot használjuk, a helyi menüben kiválasztjuk Copilot kérése lehetőséget, és beírjuk a következő kérdést:

Can you make the LINQ query in this method faster?

Borravaló

Perjelparancsokat, például /optimize, használhat, hogy segítsen jó kérdéseket megfogalmazni a Copilot számára.

Ebben a példában a Copilot a következő javasolt kódmódosításokat, valamint egy magyarázatot ad.

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

Ez a kód számos módosítást tartalmaz a lekérdezés optimalizálásához:

  • Hozzáadta a Where záradékot, és megszüntette az egyik foreach hurkot.
  • Csak a Cím tulajdonságot vetítettük előre a Select utasításban, amire ebben a példában csak szükségünk van.

Ezután újra teszteljük a profilkészítési eszközöket.

Eredmények

A kód frissítése után újra futtatjuk a CPU-használat eszközt a nyomkövetés gyűjtéséhez. A Hívásfa nézet azt mutatja, hogy GetBlogTitleX csak 1754 ms-ot fut, a proceszorra jutó összes CPU idő 37%-át felhasználva, ami jelentős javulás az 59%-hez képest.

A processzorhasználati eszköz Hívásfa nézetében látható jobb processzorhasználat képernyőképe.

Váltson a Flame Graph nézetre, hogy egy másik vizualizáció jelenjen meg, amely a javulást mutatja. Ebben a nézetben GetBlogTitleX a processzor egy kisebb részét is használja.

A processzorhasználati eszköz Flame Graph nézetében látható jobb processzorhasználat képernyőképe.

Ellenőrizze az eredményeket az Adatbázis eszköz nyomkövetésében, és 100 000 helyett csak két rekord van beolvasva ezzel a lekérdezéssel! Emellett a lekérdezés sokkal egyszerűbb, és kiküszöböli a korábban létrehozott szükségtelen LEFT JOIN-illesztéseket.

Képernyőkép az Adatbázis eszköz gyorsabb lekérdezési idejéről.

Ezután újra ellenőrizzük az eredményeket a .NET objektumlefoglalási eszközben, és láthatjuk, hogy GetBlogTitleX csak 56 000 objektumlefoglalásért felelős, közel 95% 900 000-ről!

A .NET objektumfoglalási eszköz csökkentett memóriafoglalásainak képernyőképe.

Ismételni

Előfordulhat, hogy több optimalizálásra van szükség, és a kódmódosításokkal tovább is iterálhatunk, hogy kiderüljön, mely változások javítják a teljesítményt, és segítenek csökkenteni a számítási költségeket.

Következő lépések

Az alábbi cikkek és blogbejegyzések további információkat nyújtanak a Visual Studio teljesítményeszközök hatékony használatának elsajátításához.