Giriş
dondurulmuş koleksiyonlar .NET’e yakın zamanda eklendi, FrozenDictionary
Ve FrozenSet
, merak ettim:
Öncelikle bir liste için dondurulmanın ne demek olduğunu tartışalım . Bunu da .net8 alfa sürümüyle çalışan bazı kodlarla yapıyoruz:
List<int> normalList = new List<int> { 1, 2, 3 };
ReadOnlyCollection<int> readonlyList = normalList.AsReadOnly();
FrozenSet<int> frozenSet = normalList.ToFrozenSet();
ImmutableList<int> immutableList = normalList.ToImmutableList();
normalList.Add(4);
Console.WriteLine($"List count: {normalList.Count}");
Console.WriteLine($"ReadOnlyList count: {readonlyList.Count}");
Console.WriteLine($"FrozenSet count: {frozenSet.Count}");
Console.WriteLine($"ImmutableList count: {immutableList.Count}");
Hangisi aşağıdakileri yazdıracaktır:
List count: 4
ReadOnlyList count: 4
FrozenSet count: 3
ImmutableList count: 3
Performans Durumu ve Senaryolar
Kurulum çok karmaşık değil ancak birden fazla senaryoyu test etmek istediğim için en basit şey de değil. Örnek olarak şuna odaklanmaya başladım: string
s, muhtemelen sözlük anahtarı olarak en sık kullanıldığını gördüğüm türdür, ancak çok yaygın olduğu göz önüne alındığında, belki de özel olarak buna yönelik özel optimizasyonlar vardır ve daha iyi bir anlayış için belki de diğer türlerle bazı şeyleri denemek uygun olabilir diye düşündüm. aynı zamanda (PS: şuna hızlı bir bakış: kaynak kodu optimize edilmiş özel durumların olduğu yönündeki şüpheleri doğrulamaktadır).
Yani özetle test edilen varyasyonlar:
- Farklı anahtar türleri:
string
,int
VeGuid
bunların sarılmış sürümlerinin yanı sıra (yani, gerçek bir uygulamada belirli etki alanı kavramlarını temsil edecek özel türler)- Sarmalayıcılar salt okunur kayıt yapıları olarak uygulandı
- Çeşitli miktarlarda girişler – 1, 10, 100, 1000 ve 10000
Paketleyici şuna benzer:
|
|
Temel kıyaslama uygulaması şuna benzer:
|
|
Sonuçlar
Sonuçlara!
Karşılaştırma yapmak için kurduğum senaryoların sayısı göz önüne alındığında, sonuçların çıktısı az değil, bu yüzden sadece bulguları özetleyeceğim ve buraya birkaç ekstra önemli nokta ekleyeceğim. Sonuçların tamamı için şuraya göz atabilirsiniz: GitHub deposu.
Sözlükteki girişlerin miktarına bağlı olarak bir miktar değişiklik olsa da, 100 girişli yapılandırmayı referans olarak kullanalım, çünkü bu, genel sonuçları temsil ediyor gibi görünüyor. string
S.
string
Method | N | Mean | Error | StdDev | Ratio | RatioSD | Rank |
---|---|---|---|---|---|---|---|
LookupTraditionalString | 100 | 11.9771 ns | 0.0137 ns | 0.0122 ns | 1.00 | 0.00 | 2 |
LookupTraditionalWithStronglyTypedKeyString | 100 | 30.8755 ns | 0.1421 ns | 0.1187 ns | 2.58 | 0.01 | 6 |
LookupTraditionalWithStronglyTypedKeyStringWithCustomComparer | 100 | 27.1268 ns | 0.1055 ns | 0.0881 ns | 2.26 | 0.01 | 4 |
LookupFrozenString | 100 | 4.7020 ns | 0.0071 ns | 0.0059 ns | 0.39 | 0.00 | 1 |
LookupFrozenWithStronglyTypedKeyString | 100 | 29.2874 ns | 0.2304 ns | 0.2155 ns | 2.44 | 0.02 | 5 |
LookupFrozenWithStronglyTypedKeyStringWithCustomComparer | 100 | 26.6226 ns | 0.2129 ns | 0.1992 ns | 2.22 | 0.02 | 3 |
Bu sonuçlarda açıkça görebileceğimiz gibi, sarmalayıcı tipini kullanarak string
hem geleneksel hem de dondurulmuş sözlükler için arama performansını düşürür.
Beklendiği gibi doğrudan string
Dondurulmuş sözlüğü kullanırken geleneksel sözlükle karşılaştırıldığında arama daha hızlıdır. Bununla birlikte, güçlü bir şekilde yazılan anahtar devreye girdiğinde, iki sözlük türü arasında neredeyse hiçbir fark yoktur; bu nedenle, donmuş sözlükler tarafından yapılan optimizasyonlar, özel türlere iyi bir şekilde tercüme edilmiyor gibi görünüyor (belki de özel bir türü uyarlamanın bir yolu vardır). bu optimizasyonlarla daha iyi oynadığınızdan emin değil misiniz, daha fazla araştırma yapmadınız).
Son olarak özel eşitlik karşılaştırıcısının kullanımına kısaca değinelim. Giriş sayısının birden fazla olduğu tüm kıyaslamalarda, özel anahtar için varsayılan eşitlik karşılaştırıcısından tutarlı bir şekilde daha hızlıydı, ancak görebildiğimiz gibi fark özellikle etkileyici değil.
Şimdi sonuçlara kısaca göz atalım int
.
int
Method | N | Mean | Error | StdDev | Ratio | RatioSD | Rank |
---|---|---|---|---|---|---|---|
LookupTraditionalInt | 100 | 3.1269 ns | 0.1041 ns | 0.1157 ns | 1.00 | 0.00 | 4 |
LookupTraditionalWithStronglyTypedKeyInt | 100 | 2.9621 ns | 0.0774 ns | 0.0724 ns | 0.95 | 0.03 | 3 |
LookupTraditionalWithStronglyTypedKeyIntWithCustomComparer | 100 | 3.3904 ns | 0.0658 ns | 0.0616 ns | 1.09 | 0.04 | 5 |
LookupFrozenInt | 100 | 1.3530 ns | 0.0182 ns | 0.0171 ns | 0.44 | 0.02 | 1 |
LookupFrozenWithStronglyTypedKeyInt | 100 | 2.2000 ns | 0.0214 ns | 0.0190 ns | 0.71 | 0.02 | 2 |
LookupFrozenWithStronglyTypedKeyIntWithCustomComparer | 100 | 3.1473 ns | 0.0303 ns | 0.0283 ns | 1.01 | 0.04 | 4 |
Bu sonuçlara bakıldığında, karşılaştırma yapıldığında bazı benzerliklerin olduğu görülmektedir. string
ama aynı zamanda bazı farklılıklar da var.
Yeni başlayanlar için, farklılıklar açısından tüm sonuçlar birbirine daha yakındır. int
için olduğundan string
. Ayrıca, özel türde bir anahtarın kullanımının o kadar etkili olmadığı görülüyor; geleneksel sözlük sonuçları birbirine çok yakın ve donmuş sözlük sonuçları biraz daha mesafe gösteriyor ancak yine de sayılar kadar net değil. string
S. Ek olarak, özel eşitlik karşılaştırıcısı, aşağıdaki durumlarda sayıları marjinal olarak iyileştirirken string
s, onları daha da kötüleştiriyor int
S.
arasındaki tek önemli benzerlik string
Ve int
öyle görünüyor ki her iki durumda da sarılmamış değerler için donmuş sözlük geleneksel sözlüğe göre performansı artırdı.
Şimdi nasıl olduğunu görelim Guid
karşılaştırır string
Ve int
.
Rehber
Method | N | Mean | Error | StdDev | Ratio | RatioSD | Rank |
---|---|---|---|---|---|---|---|
LookupTraditionalGuid | 100 | 3.6592 ns | 0.0609 ns | 0.0570 ns | 1.00 | 0.00 | 1 |
LookupTraditionalWithStronglyTypedKeyGuid | 100 | 4.1773 ns | 0.0143 ns | 0.0119 ns | 1.14 | 0.02 | 3 |
LookupTraditionalWithStronglyTypedKeyGuidWithCustomComparer | 100 | 4.4013 ns | 0.0726 ns | 0.0679 ns | 1.20 | 0.01 | 4 |
LookupFrozenGuid | 100 | 3.8696 ns | 0.1064 ns | 0.0944 ns | 1.06 | 0.03 | 2 |
LookupFrozenWithStronglyTypedKeyGuid | 100 | 3.6643 ns | 0.0985 ns | 0.0922 ns | 1.00 | 0.03 | 1 |
LookupFrozenWithStronglyTypedKeyGuidWithCustomComparer | 100 | 4.6254 ns | 0.0144 ns | 0.0120 ns | 1.26 | 0.02 | 5 |
Genel olarak, sonuçlar gördüğümüze yakın int
sonuçlar çok yakın, özel tür anahtarı kullanmanın çok fazla etkisi yok, ancak orada bir şey var ve karışıma özel bir eşitlik karşılaştırıcısı eklendiğinde daha da kötüleşiyor.
Şimdi nerede Guid
her ikisinden de farklıdır string
Ve int
Sonuçların hemen hemen aynı olması nedeniyle, donmuş bir sözlük kullanmanın geleneksel olana göre bir avantajı yok gibi görünüyor.
Bitirmeden önce sonuçlardan birkaç değerli söz edelim.
Diğer notlar
- 100, 1000 ve 10000 giriş için sonuçlar daha önce açıklanana benzer bir modeli izler.
- 10 giriş için dondurulmuş sözlüğün geleneksel sözlükten daha kötü performans gösterdiği çeşitli durumlar vardır
- 1 giriş için, özellikle özel türdeki anahtar senaryosu için dondurulmuş bir sözlük, geleneksel sözlüğe kıyasla yürütme süresinde daha belirgin bir azalmaya sahiptir. Bu gelişme muhtemelen 10’dan az girişe sahip sözlükler için uygulanan özel durumdan kaynaklanmaktadır.
Çıkış
Bu, bazı farklı türlerin arama performansı açısından sözlük anahtarları olarak nasıl karşılaştırıldığına hızlı bir bakış için bunu yapar; string
, int
Ve Guid
ve bunların sarılmış versiyonları.
Beklenen bazı sonuçları gördük: string
Ve int
temel arama, dondurulmuş sözlüklerle karşılaştırıldığında geleneksel sözlüklerle daha iyi performans gösteriyor; bazı beklenmedik sonuçlar da var: Guid
sözlük türünden bağımsız olarak performans çoğunlukla aynı ve ayrıca, en azından benim için, özel türleri sözlük anahtarları olarak kullanmanın etkisine ilişkin ne beklenen ne de beklenmedik sonuçlar (ne bekleyeceğime dair hiçbir fikrim yoktu, dolayısıyla tüm bu araştırma 😅).
Her zaman ilginç olan şey, bu kriterleri oluştururken ve sonuçları analiz ederken, bazı şeylerin neden bu şekilde davrandığını daha iyi anlamak için çalışma zamanının kaynak koduna, özellikle de dondurulmuş sözlüğe bakma fırsatını da kullanmasıdır. Örneğin, kodu inceleyerek sözlük boyutu, bilinen türler, özel sürümler gibi farklı faktörlere bağlı özel durumların varlığını görebiliriz. string
aynı zamanda int
ve çok daha fazlası.
Hatırlanması gereken bir diğer önemli nokta da, ortaya çıkan sayılardan da fark edebileceğiniz gibi, nanosaniye mertebesindeyiz ki bu çok çok hızlıdır. Çoğu durumda uygulama geliştiricilerin bu konuda endişelenmelerine gerek olmadığını düşünüyorum, ancak sıcak yolda bir kod parçası varsa belki de bir göz atmaya değer olabilir. Ayrıca değmese bile en azından bunları öğrenmek eğlenceli 🙂.
İlgili bağlantılar:
Uğradığınız için teşekkürler cyaz! 👋