Kodu iyileştirme ve işlem maliyetlerini azaltmaya yönelik başlangıç kılavuzu (C#, Visual Basic, C++, F#)

İşlem sürenizi azaltmak maliyetleri düşürmek anlamına gelir, bu nedenle kodunuzu iyileştirmek tasarruf sağlayabilir. Bu örnek olay incelemesi, verimliliği artırmak için profil oluşturma araçlarının nasıl kullanılacağını göstermek için performans sorunlarıyla birlikte örnek bir uygulama kullanır.

Bu örnek olay incelemesi şu konuları kapsar:

  • Kod iyileştirmenin önemi ve işlem maliyetlerini azaltma üzerindeki etkisi.
  • Uygulama performansını analiz etmek için Visual Studio profil oluşturma araçlarını kullanma.
  • Performans sorunlarını belirlemek için bu araçlar tarafından sağlanan verileri yorumlama.
  • CPU kullanımına, bellek ayırmaya ve veritabanı etkileşimlerine odaklanarak kodu iyileştirmek için pratik stratejiler uygulama.

Bu teknikleri takip edin ve daha verimli ve uygun maliyetli hale getirmek için kendi uygulamalarınıza uygulayın.

İyileştirme örnek olay incelemesi

Bu örnek olay incelemesinde incelenen örnek uygulama, blog ve blog gönderilerinden oluşan bir veritabanında sorgular çalıştıran bir .NET uygulamasıdır. SQLite yerel veritabanıyla etkileşim kurmak için .NET için popüler bir ORM (Nesne-İlişkisel Eşleme) olan Entity Framework'i kullanır. Uygulama çok sayıda sorgu yürütecek şekilde yapılandırılmıştır ve kapsamlı veri alma görevlerini işlemek için bir .NET uygulamasının gerekebileceği gerçek dünya senaryosunu benzetmektedir. Örnek uygulama, Entity Framework kullanmaya başlama örneğinin değiştirilmiş bir sürümüdür.

Örnek uygulamayla ilgili birincil performans sorunu, işlem kaynaklarını yönetme ve veritabanıyla etkileşim kurma şeklidir. Uygulamanın verimliliğini ve sonuç olarak çalıştırmayla ilişkili işlem maliyetlerini önemli ölçüde etkileyen bir performans sorunu vardır. Sorun aşağıdaki belirtileri içerir:

  • Yüksek CPU Kullanımı: Uygulamalar, gereksiz yere çok fazla CPU kaynağı tüketecek şekilde verimsiz hesaplamalar veya işleme görevleri gerçekleştirebilir. Bu, yanıt sürelerinin yavaşlanmasına ve işletim maliyetlerinin artmasına neden olabilir.

  • Verimsiz Bellek Ayırma: Uygulamalar bazen bellek kullanımı ve ayırmayla ilgili sorunlarla karşılaşabilir. .NET uygulamalarında verimsiz bellek yönetimi, atık toplamanın artmasına neden olabilir ve bu da uygulama performansını etkileyebilir.

  • Veritabanı Etkileşimi Ek Yükleri: Bir veritabanında çok sayıda sorgu yürüten uygulamalar, veritabanı etkileşimleriyle ilgili performans sorunlarıyla karşılaşabilir. Bu, verimsiz sorguları, aşırı veritabanı çağrılarını ve Entity Framework özelliklerinin kötü kullanımını içerir ve bunların tümü performansı düşürebilir.

Örnek olay incelemesi, uygulamanın performansını analiz etmek için Visual Studio'nun profil oluşturma araçlarını kullanarak bu sorunları çözmeyi amaçlar. Geliştiriciler, uygulamanın performansının nerede ve nasıl iyileştirilebileceğini anlayarak CPU kullanımını azaltmak, bellek ayırma verimliliğini artırmak, veritabanı etkileşimlerini kolaylaştırmak ve kaynak kullanımını iyileştirmek için iyileştirmeler uygulayabilir. Nihai hedef, uygulamanın genel performansını geliştirerek çalıştırmayı daha verimli ve uygun maliyetli hale getirmektir.

Sınama

Örnek .NET uygulamasındaki performans sorunlarının giderilmesi çeşitli zorluklara neden olur. Bu zorluklar, performans sorunlarını tanılamanın karmaşıklığından kaynaklandı. Açıklanan sorunları düzeltmenin başlıca zorlukları şunlardır:

  • Performans Sorunlarını Tanılama: Başlıca zorluklardan biri, performans sorunlarının kök nedenlerini doğru bir şekilde belirlemektir. Yüksek CPU kullanımı, verimsiz bellek ayırma ve veritabanı etkileşimi ek yükleri birden çok katkıda bulunan faktöre sahip olabilir. Geliştiricilerin bu sorunları tanılamak için profil oluşturma araçlarını etkili bir şekilde kullanması gerekir. Bu, bu araçların nasıl çalıştığını ve çıkışlarını nasıl yorumlayacaklarını anlamanızı gerektirir.

  • Bilgi ve Kaynak Kısıtlamaları: Son olarak, ekipler bilgi, uzmanlık ve kaynaklarla ilgili kısıtlamalarla karşılaşabilir. Bir uygulamanın profilini oluşturmak ve iyileştirmek için belirli beceriler ve deneyim gerekir ve tüm ekiplerin bu kaynaklara hemen erişimi olmayabilir.

Bu zorlukların giderilmesi için profil oluşturma araçlarının etkin kullanımını, teknik bilgileri ve dikkatli planlama ve testleri birleştiren stratejik bir yaklaşım gerekir. Örnek olay incelemesi, geliştiricilere bu süreçte yol göstermeyi, bu zorlukların üstesinden gelmek ve uygulamanın performansını geliştirmek için stratejiler ve içgörüler sağlamayı amaçlar.

Strateji

Bu örnek olay incelemesindeki yaklaşımın üst düzey bir görünümü aşağıdadır:

  • Araştırmayı CPU kullanım izlemesi alarak başlatırız. Visual Studio'nun CPU Kullanımı aracı genellikle performans araştırmalarına başlamak ve maliyeti azaltmak için kodu iyileştirmek için yararlıdır.
  • Ardından, sorunları yalıtmaya veya performansı geliştirmeye yardımcı olacak ek içgörüler elde etmek için diğer profil oluşturma araçlarından birini kullanarak bir izleme toplarız. Örneğin:
    • Bellek kullanımına göz atacağız. .NET için önce .NET Nesne Ayırma aracını deneyeceğiz. (.NET veya C++ için bunun yerine Bellek Kullanımı aracına bakabilirsiniz.)
    • ADO.NET veya Entity Framework için Veritabanı aracını kullanarak SQL sorgularını, hassas sorgu süresini ve daha fazlasını inceleyebiliriz.

Veri toplama aşağıdaki görevleri gerektirir:

  • Uygulamayı Yayın derlemesine ayarlama.
  • Performans Profili Oluşturucu'dan CPU Kullanımı aracını seçme (Alt+F2). (Sonraki adımlarda diğer araçlardan birkaçı yer alır.)
  • Performans Profili Oluşturucu'dan uygulamayı başlatın ve bir izleme toplayın.

Yüksek CPU kullanımı alanlarını inceleme

CPU Kullanımı aracıyla bir izleme topladıktan ve Visual Studio'ya yükledikten sonra, önce özetlenmiş verileri gösteren ilk .diagsession rapor sayfasını denetleriz. Rapordaki Ayrıntıları aç bağlantısını kullanın.

CPU Kullanımı aracındaki ayrıntıları açma ekran görüntüsü.

Rapor ayrıntıları görünümünde Çağrı Ağacı görünümünü açın. Uygulamadaki en yüksek CPU kullanımına sahip kod yolu, sık erişimli yol olarak adlandırılır. Sık erişimli yol alev simgesi (Sık Erişimli Yol simgesini gösteren ekran görüntüsü.) iyileştirilebilir performans sorunlarını hızla belirlemenize yardımcı olabilir.

Çağrı Ağacı görünümünde, uygulamanın CPU kullanımının yaklaşık %60'ını kullanarak uygulamadaki yöntem için GetBlogTitleX yüksek CPU kullanımı görebilirsiniz. Ancak, için GetBlogTitleX Self CPU değeri düşüktür, yalnızca yaklaşık %10'dur. Toplam CPU'dan farklı olarak, Self CPU değeri diğer işlevlerde harcanan zamanı dışlar, bu nedenle gerçek performans sorunu için çağrı ağacının daha aşağısına bakmayı biliyoruz.

CPU Kullanımı aracındaki Çağrı Ağacı görünümünün ekran görüntüsü.

GetBlogTitleX çok yüksek Self CPU değerleri tarafından kanıtlandığı gibi CPU süresinin çoğunu kullanan iki LINQ DLL'sine dış çağrılar yapar. Bu, LINQ sorgusunun iyileştirilecek bir alan olabileceğine ilişkin ilk ipucudur.

CPU Kullanımı aracında Kendi CPU'sunun vurgulandığı Arama Ağacı görünümünün ekran görüntüsü.

Görselleştirilmiş bir çağrı ağacı ve verilerin farklı bir görünümünü almak için Alev Grafiği görünümünü açın. (Alternatif olarak sağ tıklayıp GetBlogTitleX Alev Grafiğinde Görüntüle'yi de seçebilirsiniz.) Burada da yöntemin GetBlogTitleX uygulamanın CPU kullanımının büyük bir kısmından sorumlu olduğu (sarı renkle gösterilmiştir) gibi görünüyor. LINQ DLL'lerine yapılan dış çağrılar kutunun altında GetBlogTitleX gösterilir ve yöntemi için tüm CPU süresini kullanır.

CPU Kullanımı aracındaki Alev Grafiği görünümünün ekran görüntüsü.

Ek veri toplama

Genellikle, diğer araçlar analize yardımcı olmak ve sorunu yalıtmak için ek bilgiler sağlayabilir. Bu örnek olay incelemesinde aşağıdaki yaklaşımı benimsiyoruz:

  • İlk olarak bellek kullanımına bakın. Yüksek CPU kullanımı ile yüksek bellek kullanımı arasında bir bağıntı olabileceğinden, sorunu yalıtmak için her ikisine de bakmak yararlı olabilir.
  • LINQ DLL'lerini tanımladığımız için Veritabanı aracına da göz atacağız.

Bellek kullanımını denetleme

Uygulamada bellek kullanımı açısından neler olduğunu görmek için .NET Nesne Ayırma aracını kullanarak bir izleme toplarız (C++ için bunun yerine Bellek Kullanımı aracını kullanabilirsiniz). Bellek izlemesindeki Çağrı Ağacı görünümü sık erişimli yolu gösterir ve yüksek bellek kullanımı alanını tanımlamamıza yardımcı olur. Bu noktada şaşırtıcı değil, GetBlogTitleX yöntemi çok fazla nesne oluşturuyor gibi görünüyor! Aslında 900.000'den fazla nesne ayırması.

.NET Nesne Ayırma aracındaki Çağrı Ağacı görünümünün ekran görüntüsü.

Oluşturulan nesnelerin çoğu dizeler, nesne dizileri ve Int32'lerdir. Kaynak kodu inceleyerek bu türlerin nasıl oluşturulduğunu görebiliriz.

Veritabanı aracında sorguyu denetleme

Performans Profili Oluşturucu'da CPU Kullanımı yerine Veritabanı aracını seçiyoruz (veya her ikisini de seçiyoruz). bir izleme topladığımızda tanılama sayfasında Sorgular sekmesini açın. Veritabanı izlemesinin Sorgular sekmesinde, ilk satırın en uzun sorguyu (2446 ms) gösterdiğini görebilirsiniz. Kayıtlar sütunu, sorgunun kaç kayıt okuduğunu gösterir. Bu bilgileri daha sonra karşılaştırmak için kullanabilirsiniz.

Veritabanı aracındaki Veritabanı sorgularının ekran görüntüsü.

Sorgu sütununda LINQ tarafından oluşturulan deyimi inceleyerek SELECT , ilk satırı yöntemiyle GetBlogTitleX ilişkili sorgu olarak tanımlarız. Tam sorgu dizesini görüntülemek için sütun genişliğini genişletin. Tam sorgu dizesi şöyledir:

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"

Uygulamanın burada ihtiyacımızdan çok daha fazla sütun değeri aldığına dikkat edin. Şimdi kaynak koduna bakalım.

Kodu iyileştirme

Kaynak koduna göz GetBlogTitleX atma zamanı geldi. Veritabanı aracında sorguya sağ tıklayın ve Kaynak Dosyaya Git'i seçin. için kaynak kodunda, veritabanını okumak için GetBlogTitleXLINQ kullanan aşağıdaki kodu buluyoruz.

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

Bu kod, veritabanında yazar olarak "Fred Smith" ile tüm blogları aramak için döngüleri kullanır foreach . Buna baktığınızda, bellekte çok sayıda nesnenin oluşturulduğunu görebilirsiniz: veritabanındaki her blog için yeni bir nesne dizisi, her URL için ilişkili dizeler ve gönderilerde yer alan özelliklerin değerleri (blog kimliği gibi).

Biraz araştırma yapıyoruz ve LINQ sorgularını iyileştirmeye ve bu kodu bulmaya yönelik bazı yaygın öneriler buluyoruz.

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

Bu kodda, sorguyu iyileştirmeye yardımcı olmak için birkaç değişiklik yaptık:

  • yan tümcesi Where eklendi ve döngülerden foreach birini ortadan kaldırın.
  • Yalnızca deyimindeki Select Title özelliğini yansıttık. Bu örnekte ihtiyacımız olan tek şey budur.

Ardından profil oluşturma araçlarını kullanarak yeniden test edeceğiz.

Sonuçlar

Kodu güncelleştirdikten sonra, bir izleme toplamak için CPU Kullanımı aracını yeniden çalıştırırız. Çağrı Ağacı görünümü, uygulamanın CPU toplamının %37'sini kullanarak yalnızca 1754 ms çalıştığını ve %59'dan önemli bir gelişme olduğunu gösterirGetBlogTitleX.

CPU Kullanımı aracının Çağrı Ağacı görünümünde geliştirilmiş CPU kullanımının ekran görüntüsü.

İyileştirmeyi gösteren başka bir görselleştirme görmek için Alev Grafiği görünümüne geçin. Bu görünümde CPU'nun GetBlogTitleX daha küçük bir bölümünü de kullanır.

CPU Kullanımı aracının Alev Grafiği görünümünde geliştirilmiş CPU kullanımının ekran görüntüsü.

Veritabanı aracı izlemesindeki sonuçları denetleyin ve bu sorgu kullanılarak 100.000 yerine yalnızca iki kayıt okunur! Ayrıca, sorgu çok basitleştirilmiştir ve daha önce oluşturulan gereksiz LEFT JOIN'i ortadan kaldırır.

Veritabanı aracında daha hızlı sorgu süresinin ekran görüntüsü.

Daha sonra .NET Nesne Ayırma aracındaki sonuçları yeniden denetleyeceğiz ve yalnızca 56.000 nesne ayırmasından sorumlu olduğunu ve 900.000'den neredeyse %95 oranında azalma sağlandığını GetBlogTitleX göreceğiz!

.NET Nesne Ayırma aracındaki azaltılmış bellek ayırmalarının ekran görüntüsü.

Yineleme

Birden çok iyileştirme gerekebilir ve hangi değişikliklerin performansı iyileştirdiğini görmek ve işlem maliyetini azaltmaya yardımcı olmak için kod değişiklikleriyle yinelemeye devam edebiliriz.

Sonraki adımlar

Aşağıdaki makaleler ve blog gönderileri, Visual Studio performans araçlarını etkili bir şekilde kullanmayı öğrenmenize yardımcı olacak daha fazla bilgi sağlar.