Bu makale makine çevirisi ayna makalesidir, orijinal makaleye geçmek için lütfen buraya tıklayın.

Görünüm: 6846|Yanıt: 2

[Kaynak] [Dön]. .NET Performans Optimizasyonu - Collections.Pooled önerilir

[Bağlantıyı kopyala]
2022-5-29 13:45:22 tarihinde yayınlandı | | | |
Kısa giriş

Performans optimizasyonu, aynı sayıda isteğin daha az kaynakla (genellikle CPU veya bellek ve tabii ki işletim sistemi IO işlemleri, ağ trafiği, disk kullanımı vb.) işlenmesini sağlar. Ama çoğu zaman, CPU ve bellek kullanımını azaltıyoruz.
Daha önce paylaşılan içeriğin bazı sınırlamaları var, doğrudan dönüştürmek zor, bugün size basit bir yöntem paylaşmak istiyorum, sadece birkaç koleksiyon türünü değiştirmem gerekiyor, böylece performansı artırmak ve bellek ayak izini azaltmak için yeterli.
Bugün sizinle bir sınıf kütüphanesini paylaşmak istiyorum, bunuSınıf kütüphanesi Koleksiyonlar olarak adlandırılır., Adından da anlaşılacağı üzere, bu yöntem bellek ve GC'yi azaltmak amacıyla havuzlanmış bellek aracılığıyla yapılıyor ve performansının nasıl olduğunu doğrudan göreceğiz, ayrıca kaynak kodunu da göstereceğiz, neden bu performans iyileştirmelerini getirdiğini göreceğiz.

Koleksiyonlar.Birleştirilmiş

Proje bağlantısı:Bağlantı girişi görünür.

Kütüphane, System.Collections.Generic'teki sınıflara dayanmaktadır; bu sınıflar, bellek <T><T>tahsisini azaltmak, performansı artırmak ve modern API'lerle daha fazla birlikte çalışabilirlik sağlamak amacıyla yeni System.Span ve System.Buffers.ArrayPool sınıf kütüphanelerinden faydalanmak üzere değiştirilmiştir.
Collections.Pooled, .NET Standnd 2.0 (.NET Framework 4.6.1+) ile birlikte . NET Core 2.1+. CoreFX'ten kapsamlı bir birim test ve benchmark seti taşınmıştır.

Toplam test sayısı: 27501. Arka: 27501. Başarısızlık: 0. Atla: 0.
Test çalışması başarılı oldu.
Test uygulama süresi: 9.9019 saniye

Nasıl kullanılır

Bu kütüphaneyi Nuget üzerinden kolayca kurabilirsiniz, NuGet Versiyonu.

Collections.Pooled kütüphanesinde, .NET yerel tipleriyle karşılaştırmada gösterildiği gibi, yaygın kullandığımız koleksiyon türleri için havuzlu sürümleri uygular.

. .NET nativeKoleksiyonlar.BirleştirilmişAçıklama
Liste<T>PooledList<T>Genel koleksiyon sınıfları
Sözlük<TKey, TValue>PooledDictionary<TKey, TValue>Genel sözlük sınıfı
HashSet<T>PooledSet<T>Genel hash koleksiyon sınıfları
Yığın<T>Yığın<T>Genel yığınlar
Sıra<T>PooledQueue<T>Genel grup

Kullanırken sadece karşılık gelen 'yi eklememiz yeterlidir. .NET yerel sürümü ve Collections.Pooled sürümü, aşağıdaki kodda gösterildiği gibi:

Ancak, Havuzlu tipin IDispose arayüzünü uyguladığını ve kullanılan belleği Dispose() yöntemiyle havuza geri döndürdüğünü belirtmeliyiz; bu nedenle Havuzlu koleksiyon nesnesini kullandıktan sonra Dispose() yöntemini çağırmamız gerekiyor. Ya da doğrudan var anahtar kelimesini kullanabilirsin.

Not: Collections.Pooled içindeki koleksiyon nesnesini kullanınEn iyisi manuel olarak serbest bırakmaktır, ama serbest bırakmasanız da fark etmez, GC sonunda geri dönüştürür, ancak havuza geri döndürülemez ve bellek kaydetme etkisini elde etmez.
Bellek alanını yeniden kullandığı için, havuza bellek alanı dönerken, koleksiyondaki öğeleri işlemesi gerekir ve ClearMode adında bir enumeration sunar, bu durum şu şekilde tanımlanır:




Varsayılan olarak, varsayılan değer Auto kullanılabilir ve özel performans gereksinimleri varsa, riskleri bildikten sonra Never kullanabilirsiniz.
Referans tipleri ve referans türleri içeren değer tipleri için, bellek alanını havuza döndürürken dizi referansını boşaltmamız gerekir; eğer boşaldırılmazsa, GC bu bellek alanını boşaltamaz (çünkü öğenin referansı her zaman havuzda tutulmuştur), eğer saf değer tipiyse, boşaltılamaz, bu makalede referans tipleri ile struktur (değer türü) dizileri arasındaki depolama farkını açıklıyorum; saf değer tiplerinde nesne başlığı geri dönüşümü yoktur ve GC müdahalesi gerekmez.


. .NET Performans Optimizasyonu - Struct alternatif sınıflarını kullanın:Bağlantı girişi görünür.

Performans karşılaştırması

Benchmark'ı tek yapmadım ve doğrudan kullandığım açık kaynak projelerin çalışma skoru sonuçları birçok projenin bellek kullanımı açısından 0 oldu, çünkü kullanılan havuzlu bellek ekstra tahsis yoktu.

PooledList<T>

Benchmark'ta set'e eklenen 2048 elemanları tekrar edin, . .NET yerel List için <T>110us (gerçek benchmark sonuçlarına göre, şekildeki milisaniyeler bir yazım hatası olmalı) ve 263KB bellek gerektirirken, PooledList <T>sadece 36us ve 0KB belleğe ihtiyaç duyar.




PooledDictionary<TKey, TValue>

Benchmark'ta sözlüğe 10_0000 eleman ekle, . .NET yerel Sözlük<TKey, TValue> 11ms ve 13MB bellek gerektirirken, PooledDictionary<TKey, TValue> yalnızca 7ms ve 0MB bellek gerektirir.




PooledSet<T>

Benchmark'ta hash koleksiyonunu döngüye çevirin, 10_0000 eleman ekleyin, . .NET yerel HashSet <T>5348ms ve 2MB gerektirirken, PooledSet <T>sadece 4723ms ve 0MB bellek gerektirir.




PooledStack<T>

Benchmark'ta yığını döngüye çevirerek 10_0000 eleman ekleyin, . .NET yerel PooledStack <T>1079ms ve 2MB gerektirirken, PooledStack <T>yalnızca 633ms ve 0MB bellek gerektirir.




PooledQueue<T>

Benchmark'ta döngüler arasında döngü yaparak kuyruğun içine 10_0000 eleman ekleyin, . .NET yerel <T>PooledQueue 681ms ve 1MB gerektirirken, PooledQueue <T>yalnızca 408ms ve 0MB bellek gerektirir.




Sahne manuel olarak yayınlanmıyor

Ayrıca, yukarıda havuzlu koleksiyon türünün serbest bırakılması gerektiğini söyledik, ancak serbest bırakılmasa da fark etmez, çünkü GC geri dönüştürülür.


Benchmark sonuçları aşağıdaki gibidir:



Sonuç yukarıdaki Benchmark sonuçlarından çıkarılabilir.

Havuzlu tip koleksiyonunu zamanında serbest bırakmak GC'yi zar zor tetikliyor ve bellek tahsis ediyor; yukarıdaki grafikten sadece 56 Bayt bellek ayrılıyor.
Havuzlu tip koleksiyon serbest bırakılmasa bile, çünkü havuzdan bellek tahsis eder, ancak ReSize genişletme işlemi sırasında belleği tekrar kullanır ve GC ayırma belleği başlatma adımını atlar; bu nispeten hızlıdır.
En yavaş olanı normal koleksiyon türünü kullanmaktır; her ReSize genişletme işlemi yeni bellek alanı için uygulanmalıdır ve GC ayrıca önceki bellek alanını geri kazanmalıdır.


İlke analizi

Önceki blog yazımı okuduysanız, koleksiyon türleri için başlangıç boyutunu belirlemeli ve C# Sözlüğü'nün uygulama ilkesini analiz etmelisiniz, .NET BCL geliştiricilerinin bu temel koleksiyon türlerinin temel veri yapılarını yüksek performanslı rastgele erişim için dizileri olarak kullandığını bilebilirsiniz, örnek olarak List'i <T>alalım.

Eklenen öğeleri depolamak için yeni bir dizi oluşturun.
Dizide yeterli alan yoksa, genişletme işlemi iki kat daha büyük bir alan talep etmek için tetiklenir.
Yapıcı kodu şöyledir ve doğrudan oluşturulmuş genel bir dizi olduğunu görebilirsiniz:


Yani havuzlu bellek istiyorsanız, sınıf kütüphanesinde yeni anahtar kelime uygulamasının kullanıldığı yeri havuzlu uygulamayı kullanmak için değiştirmeniz yeterlidir. Burada sizinle paylaşıyorum. NET BCL, ArrayPool olarak adlandırılan bir türdür; bu tür, GC üzerindeki baskıyı azaltmak ve sık dizi oluşturma ve yok etme durumunda performansı artırmak için kullanılabilir.

Havuzlu tipimizin temel katmanı, kaynak havuzlarını paylaşmak için ArrayPool kullanmaktır ve yapıcısından, varsayılan olarak ArrayPool kullandığını <T>görebiliyoruz. Array nesnelerini atamak için paylaşılıyor ve tabii ki kendi ArrayPool'unuzu da oluşturarak bunu kullanabilirsiniz.


Ayrıca, kapasite ayarlama işlemi (genişleme) yapılırken, eski dizi iş ipliği havuzuna geri döner ve yeni dizi de havuzdan alınır.

Ayrıca, yazar Span'ı kullanarak Ekle ve Ekle gibi API'leri optimize ederek onlara daha iyi rastgele erişim performansı sağlar. Ayrıca, TryXXX serisi API eklendi, böylece daha pratik bir şekilde kullanabiliyorsunuz. Örneğin, List <T>sınıfı <T>PooledList'e kıyasla 170'e kadar modifikasyona sahiptir.



özet

Gerçek çevrimiçi kullanımımızda, yerel koleksiyon tipini Pooled tarafından sağlanan koleksiyon tipiyle değiştirebiliriz; bu da bellek kullanımını ve P95 gecikmesini azaltmada çok faydalıdır.
Ayrıca, yayınlamayı unutsanız bile, performans yerel koleksiyon türü kullanmaktan çok daha kötü olmaz. Tabii ki, en iyi alışkanlık zamanında serbest bırakmaktır.


Özgün:Bağlantı girişi görünür.




Önceki:RecyclableMemoryStream, yüksek performanslı .NET akışı sağlar
Önümüzdeki:[Pratik dövüş] Sunucu, ağ hızını test etmek için LibreSpeed geliştiriyor
2022-5-29 tarihinde 17:12:36 tarihinde yayınlandı |
Öğren
2022-6-20 09:09:22 tarihinde yayınlandı |
Karışımı öğrenin
Feragatname:
Code Farmer Network tarafından yayımlanan tüm yazılım, programlama materyalleri veya makaleler yalnızca öğrenme ve araştırma amaçları içindir; Yukarıdaki içerik ticari veya yasa dışı amaçlarla kullanılamaz, aksi takdirde kullanıcılar tüm sonuçları ödemelidir. Bu sitedeki bilgiler internetten alınmakta olup, telif hakkı anlaşmazlıklarının bu siteyle hiçbir ilgisi yoktur. Yukarıdaki içeriği indirmeden sonraki 24 saat içinde bilgisayarınızdan tamamen silmelisiniz. Programı beğendiyseniz, lütfen orijinal yazılımı destekleyin, kayıt satın alın ve daha iyi orijinal hizmetler alın. Herhangi bir ihlal olursa, lütfen bizimle e-posta yoluyla iletişime geçin.

Mail To:help@itsvse.com