LINQ sorgularında ToList () veya ToArray () öğesini çağırmak daha iyi mi?


519

Sıklıkla bir sorguyu beyan etmek istediğim yerde değerlendirmek istediğim durumla karşılaşıyorum. Bunun nedeni genellikle birkaç kez yinelemem gerektiğidir ve hesaplanması pahalıdır. Örneğin:

string raw = "...";
var lines = (from l in raw.Split('\n')
             let ll = l.Trim()
             where !string.IsNullOrEmpty(ll)
             select ll).ToList();

Bu iyi çalışıyor. Ama eğer sonucu değiştirmeyeceksem, ToArray()bunun yerine arayabilirim ToList().

Bununla birlikte ToArray(), ilk çağrı ile uygulanıp uygulanmadığını ToList()ve bu nedenle sadece çağrı yapmaktan daha az bellek verimli olup olmadığını merak ediyorum ToList().

Ben deli miyim? ToArray()Hafızanın iki kez tahsis edilmeyeceği bilgisini kullanarak sadece güvenli ve emniyetli mi aramalıyım ?


10
.NET'teki perdelerin arkasında ne olduğunu öğrenmek istiyorsanız, .NET Reflector'u
David Hedlund

32
@DavidHedlund .net kaynak kodunu öneririm .
Gqqnbig

1
Önemli bir ilişki olmasına rağmen stackoverflow.com/questions/6750447/c-toarray-performance'ın bu sorunun bir kopyası olduğunu kabul etmiyorum . Hem bellek kullanımı (bu soru) hem de performans (diğer soru) ve ilginç ve önemsiz hususlardır. Ayrı ayrı tanımlanabilirler, ancak her ikisi de birini diğerinin üzerinden seçme kararını hesaba katmalıdır. Bu sorunun cevabından herhangi birini veya diğer soruyu kapsamlı olarak öneremem. Birlikte alındığında, bir diğerinin nasıl seçileceğine dair oldukça eksiksiz bir tartışma sağlayan birkaç cevap vardır.
steve

1
@Gqqnbig - gelmiş geçmiş en faydalı yorum! Teşekkürler :-)
Mark Cooper

Yanıtlar:


366

Kullanmanız gereken diğer kısıtlamaları karşılamak için bir diziye ihtiyacınız olmadığı sürece ToList. Senaryoların çoğunda ToArraydaha fazla bellek ayıracaktır ToList.

Her ikisi de depolama için diziler kullanır, ancak ToListdaha esnek bir kısıtlamaya sahiptir. Dizinin, koleksiyondaki öğe sayısı kadar en az olması gerekir. Dizi daha büyükse, bu bir sorun değildir. Ancak ToArray, dizinin tam olarak öğe sayısına göre boyutlandırılması gerekir.

Bu kısıtlamayı karşılamak ToArraygenellikle daha fazla bir tahsisat yaparToList . Yeterince büyük bir dizi olduğunda, tam olarak doğru boyutta olan bir dizi ayırır ve öğeleri o diziye geri kopyalar. Bundan kaçınabileceği tek zaman, dizi için büyüme algoritmasının depolanması gereken elemanların sayısıyla (kesinlikle azınlıkta) çakışmasıdır.

DÜZENLE

Birkaç kişi bana fazladan kullanılmayan hafızanın List<T>değerinin sonucunu sordu .

Bu geçerli bir endişe. Oluşturulan koleksiyon uzun ömürlüdür, oluşturulduktan sonra asla değiştirilmez ve Gen2 yığınına iniş şansı yüksekse, ön tarafın ekstra tahsisini alarak daha iyi olabilirsiniz ToArray.

Genel olarak bunun daha nadir görülen bir durum olduğunu düşünüyorum. ToArrayHafızanın diğer kısa ömürlü kullanımlarına hemen aktarılan çok sayıda çağrı görmek çok daha yaygındır , bu durumdaToList açıkça daha iyidir.

Buradaki anahtar profil, profil ve daha sonra profil oluşturmaktır.


14
Öte yandan, dizinin oluşturulması için ayrılan ek bellek çöp toplama için uygun olmazken, Liste için ek yük kalır mı? Daha basit tut diyorum. Eleman eklemeniz veya çıkarmanız gerekiyorsa bunun için bir araç var. Eğer yapmazsanız, bunun için farklı bir araç var. Mantıklı olanı kullanın. Daha sonra bellek ve performansla ilgili bir sorun fark ederseniz ve işte bu , değiştirin.
Anthony Pegram

1
@AnthonyPegram evet bu geçerli bir husustur. Değer uzun süreli depolamada kullanılıyorsa, değiştirilmeyecek ve potansiyel olarak Gen 2'ye dönüştürülecekse, Gen 2 yığınını kirletmeye karşı şimdi ekstra tahsisi ödemekten daha iyi olabilirsiniz. IME bunu nadiren görüyorum. ToArray'ın hemen başka bir kısa ömürlü LINQ sorgusuna aktarıldığını görmek çok daha yaygındır.
JaredPar

2
@AnthonyPegram cevabımı tartışmanın bu tarafını içerecek şekilde güncelledim
JaredPar

8
@JaredPar Açıkçası otomatik yedek konumlara sahip ToArrayolduğu kesin konum boyutuna ihtiyaç duyuyorsa nasıl daha fazla bellek ayırabilirim anlamıyorum ToList<>. (
otomatik artış

5
@RoyiNamir, çünkü ToArray ilk olarak ToList tarzı tahsisleri ek yük ile yapar, daha sonra ek bir tam boyutlu tahsis yapar.
Timbo

169

List<T>Dinamik olarak boyutlandırılmış bir dizi olarak uygulandığından performans farkı önemsiz olacaktır . Ya çağrılması ToArray()(dahili kullanan Buffer<T>ya da dizi büyümeye sınıfı) ToList()(çağıran List<T>(IEnumerable<T>)yapıcısı) bir diziye içine koyarak ve hepsine uyacak kadar diziyi büyüyen bir mesele olmanın sona erecek.

Bu gerçeğin somut bir onayını istiyorsanız, Reflector'da söz konusu yöntemlerin uygulanmasına bakın - neredeyse aynı koda kadar kaynadıklarını göreceksiniz.


2
Karşılaştığım ilginç bir gerçek, projeksiyonunuzda bir grup birleşimi aracılığıyla tanımlanan bir grup kullanmanın neden olduğu ilişkili sorgular için, Linq to SQL'in bu grubun sayısını almak için başka bir alt sorgu eklemesine neden olmasıdır. Bu, bu durumlarda koleksiyonun boyutu öğeler alınmadan önce bilineceği anlamına gelir ve böylece sonuçları gerçekleştirirken işleme ve bellek kaynaklarından tasarruf edecek doğrudan tam boyutlu bir dizi oluşturulabilir.
jpierson

133
Sayım önceden biliniyorsa, performans aynıdır. Sayım önceden bilinmemektedir, ancak, tek farkı ToArray()ve ToList()önceki fazlalık kesim değil ikincisi ise tüm dizi kopyalamayı fazlalığını, trim sahip olduğu, ancak 25 ortalama kullanır % daha fazla bellek. Bunun yalnızca veri türü büyükse sonuçları olacaktır struct. Düşünce için sadece yiyecek.
Scott Rippey

9
@EldritchConundrum% 25 bu mantıktan gelir: Öğe sayısı bilinmiyorsa, arayarak ToListveya ToArrayküçük bir arabellek oluşturarak başlayacaktır. Bu tampon doldurulduğunda, tamponun kapasitesini iki katına çıkarır ve devam eder. Kapasite her zaman iki katına çıktığından, kullanılmayan tampon daima% 0 ila% 50 arasında olacaktır.
Scott Rippey

2
@ScottRippey IEnumerable kaynağından yeni Liste kaynağına yeni baktım ve IEnumerable'ın bir ICollection olup olmadığını kontrol eder ve eğer öyleyse, Count özelliğinden gereken tam boyutta bir dizi ayırarak başlar, bu yüzden bu ToList () kesinlikle daha hızlı olurdu. Tam bir cevap bu gerçeği içerebilir, ancak en yaygın durum olduğunu düşünmüyorum.
AndyClaw

3
Hem @AndyClaw Listve Bufferkontrol eder ICollection, bu durumda performans aynı olacaktır.
Scott Rippey

54

(yedi yıl sonra...)

Diğer birkaç (iyi) cevap, ortaya çıkacak mikroskobik performans farklılıklarına odaklanmıştır.

Bu yazı, bir dizi ( ) tarafından üretilen ve a tarafından döndürülenle karşılaştırıldığında var olan anlamsal farktan bahsetmek için yalnızca bir ektir .IEnumerator<T>T[]List<T>

En iyi örnekle gösterilmiştir:

IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

foreach (var x in source)
{
  if (x == 5)
    source[8] *= 100;
  Console.WriteLine(x);
}

Yukarıdaki kod istisnasız olarak çalışır ve çıktıyı üretir:

1
2
3
4
5
6
7
8
900
10

Bu, bir IEnumarator<int>döndürülen tarafından int[]numaralandırıcının oluşturulmasından bu yana dizinin değiştirilip değiştirilmediğini izlemediğini gösterir.

Yerel değişkeni sourcebir IList<int>. Bu şekilde, C # derleyicisinin foreachbir for (var idx = 0; idx < source.Length; idx++) { /* ... */ }döngüye eşdeğer bir şeye ifadeyi optimize etmediğinden emin olurum . Bu, var source = ...;bunun yerine kullanırsam C # derleyicisinin yapabileceği bir şeydir . .NET çerçevesinin şu anki sürümünde, burada kullanılan gerçek numaralandırıcı herkese açık olmayan bir başvuru tipidir, System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]ancak elbette bu bir uygulama detayıdır.

Ben değiştirmek Şimdi, eğer .ToArray()içine .ToList(), sadece alıyorum:

1
2
3
4
5

ardından bir System.InvalidOperationExceptionpatlama:

Koleksiyon değiştirildi; numaralandırma işlemi yürütülemeyebilir.

Bu durumda altta yatan numaralandırıcı genel değişken değer tipidir System.Collections.Generic.List`1+Enumerator[System.Int32]( IEnumerator<int>kullandığım için bu durumda bir kutunun içinde kutulu IList<int>).

Sonuç olarak, numaralandırmaList<T>sırasında üretilen numaralandırıcı listenin numaralandırma sırasında değişip değişmediğini takip ederken, ürettiği numaralandırıcıT[]değişmez. Bu yüzden.ToList()vearasında seçim yaparken bu farkı göz önünde bulundurun.ToArray().

İnsanlar genelde tek eklemek ekstra .ToArray() ya .ToList()da Bir numaralandırıcının yaşam süresi boyunca değiştirildiği konusunda izler bir koleksiyon atlatmak için.

(Kimsenin bilmesini istiyorsa nasılList<> toplama değiştirildiği konusunda izler, özel alan vardır _versionher şey değiştirilir Bu sınıfta List<>güncellenir.)


28

@Mquander ile performans farkının önemsiz olması konusunda hemfikirim. Ancak, emin olmak için karşılaştırmak istedim, bu yüzden yaptım - ve önemsiz.

Testing with List<T> source:
ToArray time: 1934 ms (0.01934 ms/call), memory used: 4021 bytes/array
ToList  time: 1902 ms (0.01902 ms/call), memory used: 4045 bytes/List

Testing with array source:
ToArray time: 1957 ms (0.01957 ms/call), memory used: 4021 bytes/array
ToList  time: 2022 ms (0.02022 ms/call), memory used: 4045 bytes/List

Her kaynak dizinin / Listenin 1000 öğesi vardı. Böylece hem zaman hem de bellek farklılıklarının göz ardı edilebilir olduğunu görebilirsiniz.

Sonuç olarak: ToList () ' i de kullanabilirsiniz , çünkü List<T>bir dizi bayt bellek sizin için gerçekten önemli olmadıkça, bir diziden daha fazla işlevsellik sağlar.


1
structİlkel tip veya sınıf yerine büyük bir sonuç kullansanız bu sonucun farklı olup olmayacağını merak ediyorum .
Scott Rippey

12
Liste <T> .ToList ???? Ne anlamı? ICollection arabirimini uygulamayan bir IEnumerable vermeye çalışsak iyi olur.
Grigory

8
Herhangi birinin numaralandırmasını değil, yalnızca zamanını ToListveya ToArrayçağrısını ölçtüğümden emin olmak istedim IEnumerable. Liste <T> .ToList () hala yeni bir Liste <T> oluşturur - bu yalnızca "bunu döndürmez".
EMP

23
-1 davranışları ToArray()ve ToList()onlar ile birlikte zaman çok fazla farklılık ICollection<T>Onlar sadece tek bir tahsisini ve tek kopyalama işlemi yapmak - parametresi. Hem uygula List<T>hem de Arrayuygula ICollection<T>, bu yüzden karşılaştırmalı değerlendirmeleriniz hiç geçerli değil.
Muhammed Dehghan

1
İlgilenen herkes için kendi ölçütümü ayrı bir cevap olarak yayınladım . Uygulama probleminden .Select(i => i)kaçınmak için kullanır ve ilk etapta ICollection<T>kaynak üzerinde tekrarlamanın ne kadar zaman aldığını görmek için bir kontrol grubu içerir IEnumerable<>.
StriplingWarrior

19

ToList()IEnumerable<T>(örneğin ORM'den) kullanıyorsanız genellikle tercih edilir . Sekansın uzunluğu başlangıçta bilinmiyorsa, ToArray()List gibi dinamik uzunluklu bir koleksiyon oluşturur ve daha sonra ekstra zaman alan diziye dönüştürür.


26
Bu durumda okunabilirliğin performansı düşürdüğüne karar verdim. Artık ToList'i yalnızca eleman eklemeye devam etmeyi beklediğimde kullanıyorum. Diğer tüm durumlarda (çoğu durumda), ToArray kullanıyorum. Ama girdi için teşekkürler!
Frank Krueger

5
ILSpy'a bakarak, Enumerable.ToArray()çağırır new Buffer<TSource>(source).ToArray(). Buffer yapıcısında, kaynak ICollection uygularsa, source.CopyTo (items, 0) öğesini çağırır ve .ToArray (), iç öğeler dizisini doğrudan döndürür. Dolayısıyla, bu durumda fazladan zaman alan bir dönüşüm yoktur. Kaynak ICollection uygulamıyorsa, ToArray, Scott Rippey'in yukarıdaki yorumu tarafından açıklandığı gibi dizinin sonundan fazladan kullanılmayan konumları kırpmak için bir dizi kopyasına neden olacaktır.
BrandonAGr

19

Bellek her zaman iki kez tahsis edilir - veya buna yakın bir şey. Bir diziyi yeniden boyutlandıramayacağınız için, her iki yöntem de büyüyen bir koleksiyondaki verileri toplamak için bir tür mekanizma kullanır. (Eh, Liste kendi içinde büyüyen bir koleksiyon.)

Liste, dahili depolama olarak bir dizi kullanır ve gerektiğinde kapasiteyi iki katına çıkarır. Bu, öğelerin ortalama 2 / 3'ünün en az bir kez yeniden tahsis edildiği, en az iki kez yeniden tahsis edilenlerin yarısının, en az üç kez bunların yarısının vb. Bu, her bir öğenin ortalama olarak 1,3 kez yeniden tahsis edildiği anlamına gelir, bu da çok fazla yük değildir.

Ayrıca, dize topluyorsanız koleksiyonun kendisinin yalnızca dizelere referanslar içerdiğini, dizelerin kendilerinin yeniden tahsis edilmediğini unutmayın.


Bu sormak cahil bir şey olabilir, ancak özetlediğiniz 2/3, 1/3, 1/6 mantığı Listenin dizisinin yerinde genişletilebileceğini varsaymıyor mu? Yani, dizinin sonunda boş alan var, böylece mevcut tahsisin taşınması gerekmez mi?

@JonofAllTrades: Hayır, dizi hiçbir zaman yerinde genişletilmez, .NET'teki bellek yönetimi bunu yapmaz. Yerinde uzatılırsa, öğelerin yeniden tahsis edilmesine gerek kalmaz.
Guffa

Ah, anlıyorum: yeniden tahsis edilmeyen öğelerin, son tahsisde oldukları için bunu yapmak zorunda değildi. Önceki ayırmalarda ayrılan tüm öğeler taşınır, ancak dizi uzunluğundaki logaritmik artışlar nedeniyle bu hesaplanabilir bir kesirdir. Açıkladığınız için teşekkürler!

19

Dışarıda 2020 ve herkes .NET Core 3.1 kullanıyor, bu yüzden Benchmark.NET ile bazı karşılaştırmalar yapmaya karar verdim.

TL; DR: ToArray () performans açısından daha iyidir ve koleksiyonu değiştirmeyi planlamıyorsanız daha iyi bir iş iletme amacı taşır.


    [MemoryDiagnoser]
    public class Benchmarks
    {
        [Params(0, 1, 6, 10, 39, 100, 666, 1000, 1337, 10000)]
        public int Count { get; set; }

        public IEnumerable<int> Items => Enumerable.Range(0, Count);

        [Benchmark(Description = "ToArray()", Baseline = true)]
        public int[] ToArray() => Items.ToArray();

        [Benchmark(Description = "ToList()")]
        public List<int> ToList() => Items.ToList();

        public static void Main() => BenchmarkRunner.Run<Benchmarks>();
    }

Sonuçlar:


    BenchmarkDotNet=v0.12.0, OS=Windows 10.0.14393.3443 (1607/AnniversaryUpdate/Redstone1)
    Intel Core i5-4460 CPU 3.20GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
    Frequency=3124994 Hz, Resolution=320.0006 ns, Timer=TSC
    .NET Core SDK=3.1.100
      [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
      DefaultJob : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT


    |    Method | Count |          Mean |       Error |      StdDev |        Median | Ratio | RatioSD |   Gen 0 | Gen 1 | Gen 2 | Allocated |
    |---------- |------ |--------------:|------------:|------------:|--------------:|------:|--------:|--------:|------:|------:|----------:|
    | ToArray() |     0 |      7.357 ns |   0.2096 ns |   0.1960 ns |      7.323 ns |  1.00 |    0.00 |       - |     - |     - |         - |
    |  ToList() |     0 |     13.174 ns |   0.2094 ns |   0.1958 ns |     13.084 ns |  1.79 |    0.05 |  0.0102 |     - |     - |      32 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     1 |     23.917 ns |   0.4999 ns |   0.4676 ns |     23.954 ns |  1.00 |    0.00 |  0.0229 |     - |     - |      72 B |
    |  ToList() |     1 |     33.867 ns |   0.7350 ns |   0.6876 ns |     34.013 ns |  1.42 |    0.04 |  0.0331 |     - |     - |     104 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |     6 |     28.242 ns |   0.5071 ns |   0.4234 ns |     28.196 ns |  1.00 |    0.00 |  0.0280 |     - |     - |      88 B |
    |  ToList() |     6 |     43.516 ns |   0.9448 ns |   1.1949 ns |     42.896 ns |  1.56 |    0.06 |  0.0382 |     - |     - |     120 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    10 |     31.636 ns |   0.5408 ns |   0.4516 ns |     31.657 ns |  1.00 |    0.00 |  0.0331 |     - |     - |     104 B |
    |  ToList() |    10 |     53.870 ns |   1.2988 ns |   2.2403 ns |     53.415 ns |  1.77 |    0.07 |  0.0433 |     - |     - |     136 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |    39 |     58.896 ns |   0.9441 ns |   0.8369 ns |     58.548 ns |  1.00 |    0.00 |  0.0713 |     - |     - |     224 B |
    |  ToList() |    39 |    138.054 ns |   2.8185 ns |   3.2458 ns |    138.937 ns |  2.35 |    0.08 |  0.0815 |     - |     - |     256 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   100 |    119.167 ns |   1.6195 ns |   1.4357 ns |    119.120 ns |  1.00 |    0.00 |  0.1478 |     - |     - |     464 B |
    |  ToList() |   100 |    274.053 ns |   5.1073 ns |   4.7774 ns |    272.242 ns |  2.30 |    0.06 |  0.1578 |     - |     - |     496 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |   666 |    569.920 ns |  11.4496 ns |  11.2450 ns |    571.647 ns |  1.00 |    0.00 |  0.8688 |     - |     - |    2728 B |
    |  ToList() |   666 |  1,621.752 ns |  17.1176 ns |  16.0118 ns |  1,623.566 ns |  2.85 |    0.05 |  0.8793 |     - |     - |    2760 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1000 |    796.705 ns |  16.7091 ns |  19.8910 ns |    796.610 ns |  1.00 |    0.00 |  1.2951 |     - |     - |    4064 B |
    |  ToList() |  1000 |  2,453.110 ns |  48.1121 ns |  65.8563 ns |  2,460.190 ns |  3.09 |    0.10 |  1.3046 |     - |     - |    4096 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() |  1337 |  1,057.983 ns |  20.9810 ns |  41.4145 ns |  1,041.028 ns |  1.00 |    0.00 |  1.7223 |     - |     - |    5416 B |
    |  ToList() |  1337 |  3,217.550 ns |  62.3777 ns |  61.2633 ns |  3,203.928 ns |  2.98 |    0.13 |  1.7357 |     - |     - |    5448 B |
    |           |       |               |             |             |               |       |         |         |       |       |           |
    | ToArray() | 10000 |  7,309.844 ns | 160.0343 ns | 141.8662 ns |  7,279.387 ns |  1.00 |    0.00 | 12.6572 |     - |     - |   40064 B |
    |  ToList() | 10000 | 23,858.032 ns | 389.6592 ns | 364.4874 ns | 23,759.001 ns |  3.26 |    0.08 | 12.6343 |     - |     - |   40096 B |

    // * Hints *
    Outliers
      Benchmarks.ToArray(): Default -> 2 outliers were removed (35.20 ns, 35.29 ns)
      Benchmarks.ToArray(): Default -> 2 outliers were removed (38.51 ns, 38.88 ns)
      Benchmarks.ToList(): Default  -> 1 outlier  was  removed (64.69 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (67.02 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (130.08 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  detected (541.82 ns)
      Benchmarks.ToArray(): Default -> 1 outlier  was  removed (7.82 us)

    // * Legends *
      Count     : Value of the 'Count' parameter
      Mean      : Arithmetic mean of all measurements
      Error     : Half of 99.9% confidence interval
      StdDev    : Standard deviation of all measurements
      Median    : Value separating the higher half of all measurements (50th percentile)
      Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
      RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
      Gen 0     : GC Generation 0 collects per 1000 operations
      Gen 1     : GC Generation 1 collects per 1000 operations
      Gen 2     : GC Generation 2 collects per 1000 operations
      Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
      1 ns      : 1 Nanosecond (0.000000001 sec)

1
Koleksiyonu değiştirmeyi planlamıyorsanız, niyetin daha iyi gösterilebileceğini düşünüyorum ToImmutableArray()(System.Collections.Immutable paketinden) 😉
Arturo Torres Sánchez

@ ArturoTorresSánchez doğru, ama koleksiyon bir yöntem dışında maruz kalmazsa, ben sadece bir dizi kullanırdım.
Tyrrrz

2
Bunun için teşekkürler. Seçilen cevap sadece bir argüman ve bu argümanın ardından sonuçların alınmasıdır. Bunu bilimsel olarak yapmak ve bir bonus olarak ne kadar fark olduğunu bilmek için bilmenin tek bir yolu var.
Jonas

15

Düzenle : Bu cevabın son kısmı geçerli değil. Ancak, geri kalanı hala yararlı bilgiler, bu yüzden bırakacağım.

Bunun eski bir yazı olduğunu biliyorum, ama aynı soruyu yaptıktan ve biraz araştırma yaptıktan sonra, paylaşmaya değer olabilecek ilginç bir şey buldum.

İlk olarak, mquander ve cevabına katılıyorum. Performans açısından, ikisinin aynı olduğunu söylerken haklı.

Ancak, yansıtıcı System.Linq.Enumerablead alanındaki yöntemlere bakmak için Reflektör kullanıyorum ve çok yaygın bir optimizasyon fark ettim.
Mümkün olduğunda, IEnumerable<T>kaynak yönteme IList<T>veya ICollection<T>yöntemi optimize etmek için kullanılır. Örneğin, şuna bakın ElementAt(int).

İlginçtir, Microsoft sadece için optimizasyon yapmayı seçti IList<T>, ancak seçmedi IList. Microsoft'un IList<T>arayüzü kullanmayı tercih ettiği anlaşılıyor .

System.Arrayyalnızca uygular IList, bu nedenle bu uzantı optimizasyonlarından hiçbirinden yararlanamaz.
Bu nedenle, en iyi uygulamanın .ToList()yöntemi kullanmak olduğunu beyan ederim .
Uzantı yöntemlerinden herhangi birini kullanırsanız veya listeyi başka bir yönteme iletirseniz, bunun için bir optimize etme olasılığı vardır IList<T>.


16
Bir test yaptım ve şaşırtıcı bir şey buldum. Bir dizi IList <T> uygular! System.Array'i analiz etmek için Reflector'u kullanarak yalnızca IList, ICollection, IEnumerable kalıtım zincirini ortaya çıkarır, ancak çalışma zamanı yansımasını kullanarak [] dizesinin IList, ICollection, IEnumerable, IList <string>, ICollection <string miras zincirine sahip olduğunu öğrendim >, IEnumerable <string>. Bu nedenle, mquander'dan daha iyi bir cevabım yok!
Scott Rippey

@ScottRippey Evet. Fark ettiğiniz garip gözlem aslında bir "hack" in bir parçasıdır - ve "sabit boyutlu" ve benzer özelliklerle ilgili bazı garip sonuçları da vardır (onu nasıl kullandığınıza bağlı olarak bazı tutarsızlıklar ile). .Net kaynak kodunun içinde bu konuya değinen bazı büyük yorumlar var. Bağlamadığım için üzgünüm, ancak doğru hatırlıyorsam (dizi sınıfının içinde) bulmak oldukça kolaydır. (Ve ayrıca tutarsızlıkları tartışan büyük bir SO sorusu var ... bir yerde ...> __>)
AnorZaken

@ScottRippey sadece FYI Yorumunuzla ilgili bu cevabı buldum: stackoverflow.com/a/4482567/2063755
David Klempfner

14

İnsanların burada yapmadıkları diğer ölçütleri buldum, işte benim çatlamam. Metodolojimle ilgili bir sorun bulursanız bana bildirin.

/* This is a benchmarking template I use in LINQPad when I want to do a
 * quick performance test. Just give it a couple of actions to test and
 * it will give you a pretty good idea of how long they take compared
 * to one another. It's not perfect: You can expect a 3% error margin
 * under ideal circumstances. But if you're not going to improve
 * performance by more than 3%, you probably don't care anyway.*/
void Main()
{
    // Enter setup code here
    var values = Enumerable.Range(1, 100000)
        .Select(i => i.ToString())
        .ToArray()
        .Select(i => i);
    values.GetType().Dump();
    var actions = new[]
    {
        new TimedAction("ToList", () =>
        {
            values.ToList();
        }),
        new TimedAction("ToArray", () =>
        {
            values.ToArray();
        }),
        new TimedAction("Control", () =>
        {
            foreach (var element in values)
            {
                // do nothing
            }
        }),
        // Add tests as desired
    };
    const int TimesToRun = 1000; // Tweak this as necessary
    TimeActions(TimesToRun, actions);
}


#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
    Stopwatch s = new Stopwatch();
    int length = actions.Length;
    var results = new ActionResult[actions.Length];
    // Perform the actions in their initial order.
    for (int i = 0; i < length; i++)
    {
        var action = actions[i];
        var result = results[i] = new ActionResult { Message = action.Message };
        // Do a dry run to get things ramped up/cached
        result.DryRun1 = s.Time(action.Action, 10);
        result.FullRun1 = s.Time(action.Action, iterations);
    }
    // Perform the actions in reverse order.
    for (int i = length - 1; i >= 0; i--)
    {
        var action = actions[i];
        var result = results[i];
        // Do a dry run to get things ramped up/cached
        result.DryRun2 = s.Time(action.Action, 10);
        result.FullRun2 = s.Time(action.Action, iterations);
    }
    results.Dump();
}

public class ActionResult
{
    public string Message { get; set; }
    public double DryRun1 { get; set; }
    public double DryRun2 { get; set; }
    public double FullRun1 { get; set; }
    public double FullRun2 { get; set; }
}

public class TimedAction
{
    public TimedAction(string message, Action action)
    {
        Message = message;
        Action = action;
    }
    public string Message { get; private set; }
    public Action Action { get; private set; }
}

public static class StopwatchExtensions
{
    public static double Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Restart();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.Elapsed.TotalMilliseconds;
    }
}
#endregion

Şunları yapabilirsiniz buraya LINQPad Komut indirmek .

Sonuçlar: ToArray vs ToList performansı

Yukarıdaki kodu değiştirerek şunları keşfedeceksiniz:

  1. Daha küçük dizilerle uğraşırken fark daha az önemlidir . Daha fazla yineleme, ancak daha küçük diziler
  2. Fark ints ile uğraşırken s yerine daha az belirgindir string.
  3. structS yerine büyük s kullanmak stringgenellikle çok daha fazla zaman alır, ancak oranı gerçekten fazla değiştirmez.

Bu, en çok oy alan cevapların sonuçlarına katılıyor:

  1. Kodunuz sık sık birçok büyük veri listesi üretmedikçe, bir performans farkı fark etmeniz olası değildir. (100K dizesinden 1000 liste oluştururken yalnızca 200 ms fark vardı.)
  2. ToList() tutarlı bir şekilde daha hızlı çalışır ve sonuçlara uzun süre devam etmeyi planlamıyorsanız daha iyi bir seçim olacaktır.

Güncelleme

@JonHanna Select, bir ToList()veya ToArray()uygulamanın uygulanmasına bağlı olarak ortaya çıkan koleksiyonun boyutunu önceden tahmin etmesinin mümkün olduğuna dikkat çekti . .Select(i => i)Yukarıdaki kodun değiştirilmesi şu anda Where(i => true) çok benzer sonuçlar vermektedir ve .NET uygulamasından bağımsız olarak bunu yapmak daha olasıdır.

Select yerine Where kullanarak karşılaştırma


O boyutu olacak fark edeceksiniz çünkü .NET Çekirdek her iki olgu, daha iyi burada netfx üzerinde daha olmalı 100000ve optimize etmek kullanımı her iki ToList()ve ToArray()birlikte ToArray()o gerekir ancak küçültme işlemi gerekmez çünkü çok hafif hafif olmak Aksi takdirde, tek yer ToList()avantajı vardır. Sorudaki örnek yine de kaybolacaktı, çünkü bu Wheretür bir boyut tahmini yapılamaz.
Jon Hanna

@JonHanna: Hızlı geri bildirim için teşekkürler. .NET Core'un bu optimizasyonu yaptığını bilmiyordum. Çok havalı. Benim kod, .Select(i => i)bunun .Where(i => true)için düzeltmek ile değiştirilebilir .
StriplingWarrior

Evet, bu corefx üzerinde onu etkileyen optimizasyonu durduracaktır. Hem ToArray()yukarıdaki gibi bir avantaj ( avantaj sağlayacak) hem de yukarıdaki gibi olmayan bir boyuta sahip olmak ve sonuçları karşılaştırmak ilginç olabilir .
Jon Hanna

@JonHanna: İlginçtir, ToArray() hala en iyi senaryoda kaybediyor . Math.Pow(2, 15)Elemanlar ile (ToList: 700ms, ToArray: 900ms). Bir eleman daha eklemek (ToList: 925, ToArray: 1350) öğesine çarpıyor. ToArrayZaten mükemmel boyutta olsa bile diziyi kopyalayıp kopyalamadığını merak ediyorum. Muhtemelen, ekstra şartlı değerinin yeterince nadir bir olay olduğunu anladılar.
StriplingWarrior

Corefx'te optimize etmeye başlamadan önce tam boyutta eşleme yapmıyordu, bu yüzden en fazla mola verdikleri durum bu.
Jon Hanna

12

Kararınızı , tasarım seçiminin ideal olarak ne olduğuna ToListveya ona ToArraygöre belirlemelisiniz. Yalnızca dizine göre yinelenebilen ve erişilebilen bir koleksiyon istiyorsanız, seçeneğini belirleyin ToArray. Daha sonra çok fazla uğraşmadan koleksiyona ekleme ve kaldırma ek yetenekleri istiyorsanız, o zaman bir ToList(gerçekten bir diziye ekleyemezsiniz, ancak bu genellikle doğru araç değildir).

Performans önemliyse, neyin daha hızlı çalışacağını da düşünmelisiniz. Gerçekçi, aramak olmaz ToListya ToArraymilyonlarca kez, ama belki elde toplanmasıyla ilgili milyonlarca kez çalışır. Bu açıdan []bu yana, daha da List<>olan []bazı giderler ile. Bazı verimlilik karşılaştırması için bu konuya bakın: Hangisi daha verimli: List <int> veya int []

Bir süre önce kendi testlerimde ToArraydaha hızlı bulmuştum . Ve testlerin ne kadar çarpık olduğundan emin değilim. Performans farkı o kadar önemsizdir ki bu, yalnızca bu sorguları milyonlarca kez çalıştırıyorsanız fark edilebilir.


2
Evet - derleyici bir dizi (IEnumerable <> yerine) üzerinde yineleme yaptığınızı biliyorsa, yinelemeyi önemli ölçüde optimize edebilir.
RobSiklos

12

Çok geç bir cevap ama bence googlers için yararlı olacaktır.

Linq kullanarak yarattıklarında ikisi de emer. Her ikisi de gerekirse arabelleği yeniden boyutlandırmak için aynı kodu kullanır . ToArraydahili IEnumerable<>olarak 4 öğeden oluşan bir dizi ayırarak diziye dönüştürmek için bir sınıf kullanır . Bu yeterli değilse, yeni bir dizi oluşturarak boyutu iki katına çıkarır ve geçerli dizinin çiftini kopyalar. Sonunda öğelerinizin yeni bir sayım dizisini ayırır. Sorgunuz 129 öğe döndürürse, ToArray, 256 öğe dizisi oluşturmak için 6 ayırma ve bellek kopyalama işlemi gerçekleştirir ve geri döndürülecek başka bir 129 dizisi olur. bellek verimliliği için çok fazla.

ToList aynı şeyi yapar, ancak gelecekte öğe ekleyebileceğiniz için son ayırmayı atlar. Liste, bir linq sorgusundan mı yoksa manuel olarak mı oluşturulduğunu umursamaz.

Oluşturma için Liste bellekle daha iyidir, ancak cpu ile daha da kötüsü, liste genel bir çözüm olduğundan, her eylem .net'in iç aralık dizilerine ek aralık kontrolleri gerektirir.

Sonuç kümenizde çok fazla yineleme yapacaksanız, listelerden daha az aralık kontrolü anlamına geldiğinden diziler iyidir ve derleyiciler genellikle dizileri sıralı erişim için optimize eder.

Listenin başlatma tahsisi, oluştururken kapasite parametresini belirtirseniz daha iyi olabilir. Bu durumda, sonuç boyutunu bildiğinizi varsayarak diziyi yalnızca bir kez ayıracaktır. ToListof linq, onu sağlamak için bir aşırı yük belirtmez, bu nedenle verilen kapasiteye sahip bir liste oluşturan ve daha sonra kullanan uzantı yöntemimizi oluşturmamız gerekir List<>.AddRange.

Bu cevabı bitirmek için aşağıdaki cümleleri yazmam gerekiyor

  1. Sonunda, bir ToArray veya ToList kullanabilirsiniz, performans o kadar farklı olmaz (@EMP'nin cevabına bakın).
  2. C # kullanıyorsunuz. Performansa ihtiyacınız varsa, yüksek performanslı kod hakkında yazma konusunda endişelenmeyin, ancak kötü performans kodu yazma konusunda endişelenmeyin.
  3. Yüksek performanslı kod için daima x64'ü hedefleyin. AFAIK, x64 JIT, C ++ derleyicisini temel alır ve kuyruk özyineleme optimizasyonları gibi komik şeyler yapar.
  4. 4.5 ile profil yönlendirmeli optimizasyonun ve çok çekirdekli JIT'in keyfini çıkarabilirsiniz.
  5. Sonunda, daha hızlı işlemek için async / await desenini kullanabilirsiniz.

İkisi de berbat mı? Yedek bellek tahsisi gerektirmeyen alternatif bir fikriniz var mı?
nawfal

Soru bağlamında, evet, ikisi de berbat ama gereksiz tahsisler nedeniyle ve başka bir şey değil. Gereksiz tahsisi azaltmak için bağlantılı listeler bellek ve yineleme hızı pahasına kullanılabilir. Günün sonunda yaptığımız bu, takas yapıyoruz. 200 (örneğin) kapasiteli bir liste oluşturmak ve sonra öğeleri yüklemek başka bir fikir. Bu aynı zamanda fazlalığı da azaltacaktır, ancak diziler her zaman daha hızlıdır, bu da başka bir değiş tokuştur.
Erdoğan Kurtur

200 listesi oluşturulsun mu? Yani belki boyutlandırma önlemek, ama ben kullanılan gereksiz bellek hakkında konuşuyordu. Buna yardım edemezsiniz, çünkü boyutun ne olabileceği hakkında ön bilgi yoktur. Kapasiteyi zaten bir a'nın yapıcısında belirtebilirsiniz List<T>, ancak yapmadığınızda veya yapamadığınızda, yardım edemezsiniz.
nawfal

2
bellekteki tek artık veri, işaretçilerin listesi olan dizinin içeriğidir (bu durumda). bir milyon 64 bit işaretçi 8 MB kadar bellek alır, bu da işaret ettikleri bir milyon nesneye kıyasla bir şey değildir. 200 yalnızca bir sayıdır ve yeniden boyutlandırma çağrılarının sayısını en fazla 5 kez azaltma şansına sahiptir. ve evet, yardım edemeyiz. daha iyi seçeneklerimiz yok. Daha iyi bir çözümüm yok, ama bu sorunun nerede olduğunu söylememe izin verilmediğim anlamına gelmiyor.
Erdoğan Kurtur

1
hmm sonunda çizgiyi çizdiğiniz yer. Mevcut uygulamayı seviyorum. Cevabınızın tonu bana sorunun nerede olduğu eleştirisi olduğunu düşündürdü :)
nawfal

7

Bu eski bir sorudur - ama yanılmak isteyen kullanıcıların yararı için, bir Linq ifadesinin önbelleğe alınması ve birden fazla numaralandırmasının durdurulması etkisi olan Numaralandırmanın 'Hatırlanması' da bir alternatifi vardır (ToArray () ve ToList () yöntemi, listenin veya dizinin toplama öznitelikleri hiç kullanılmamasına rağmen çok kullanılır.

Memoize RX / System.Interactive lib dosyasında mevcuttur ve burada açıklanmıştır: System.Interactive ile daha fazla LINQ

(Gönderen Bart De'Smet blogunda bir olan yüksek sen Objects Linq ile çok çalışıyorsanız okumanız önerilir)


4

Bir seçenek, bir salt okunur döndüren kendi uzantı yönteminizi eklemektir ICollection<T>. Bu kullanmaktan daha iyi olabilir ToListveya ToArraybir dizi / listenin indeksleme özelliklerini birini kullanın veya bir listeden / kaldırmayı eklemek istemiyorum.

public static class EnumerableExtension
{
    /// <summary>
    /// Causes immediate evaluation of the linq but only if required.
    /// As it returns a readonly ICollection, is better than using ToList or ToArray
    /// when you do not want to use the indexing properties of an IList, or add to the collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumerable"></param>
    /// <returns>Readonly collection</returns>
    public static ICollection<T> Evaluate<T>(this IEnumerable<T> enumerable)
    {
        //if it's already a readonly collection, use it
        var collection = enumerable as ICollection<T>;
        if ((collection != null) && collection.IsReadOnly)
        {
            return collection;
        }
        //or make a new collection
        return enumerable.ToList().AsReadOnly();
    }
}

Birim testleri:

[TestClass]
public sealed class EvaluateLinqTests
{
    [TestMethod]
    public void EvalTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResult = list.Select(i => i);
        var linqResultEvaluated = list.Select(i => i).Evaluate();
        list.Clear();
        Assert.AreEqual(0, linqResult.Count());
        //even though we have cleared the underlying list, the evaluated list does not change
        Assert.AreEqual(3, linqResultEvaluated.Count());
    }

    [TestMethod]
    public void DoesNotSaveCreatingListWhenHasListTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //list is not readonly, so we expect a new list
        Assert.AreNotSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasReadonlyListTest()
    {
        var list = new List<int> {1, 2, 3}.AsReadOnly();
        var linqResultEvaluated = list.Evaluate();
        //list is readonly, so we don't expect a new list
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    public void SavesCreatingListWhenHasArrayTest()
    {
        var list = new[] {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        //arrays are readonly (wrt ICollection<T> interface), so we don't expect a new object
        Assert.AreSame(list, linqResultEvaluated);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantAddToResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Add(4);
    }

    [TestMethod]
    [ExpectedException(typeof (NotSupportedException))]
    public void CantRemoveFromResultTest()
    {
        var list = new List<int> {1, 2, 3};
        var linqResultEvaluated = list.Evaluate();
        Assert.AreNotSame(list, linqResultEvaluated);
        linqResultEvaluated.Remove(1);
    }
}

Salt okunur toplama sözleşmesinin yalnızca nesnenin kullanıcısının onu değiştiremeyebileceğini belirtmesine dikkat etmek gerekir, ancak sahibi, değiştirilebilir bir arayüz sunan bir referans tutarsa ​​bunu yine de yapabilir. Altta yatan yapının asla değişmeyeceğini garanti eden arayüzler için değişmez koleksiyonlara bakın. Değişmez veya salt okunur veya düz okuma-yazma koleksiyonlarının neden daha iyi veya daha kötü olduğu konusunda karşılaştırma için bir referans noktasına ihtiyaç vardır; nihai bir cevap yoktur (başka bir şey seçmek zorunda kalmazdık).
tne

@tne Not AsReadOnly'den önce Tolist yapıyorum, bu yüzden altta yatan mutable için referans yok.
Weston

Tamamen haklısınız ve bu muhtemelen değişmez koleksiyonlar BCL'ye gelmeden önce bir şeyler yapmanın en iyi yoluydu (ilk betanın cevabınızdan bir ay sonra ortaya çıktığını görüyorum).
tne

İş parçacığı güvenliği için, iş parçacıklarının değişmeyeceğini varsayabilecekleri değişmez koleksiyonlar vardır ve eğer öyleyse, okuyuculara karşı yarışmak ve onu kullanırken değiştirmek yerine yeni bir sürüm oluşturulur. Bu şekilde, hiç kimsenin bir kilit almasına gerek kalmaz.
doug65536

4

ToListAsync<T>() tercih edilir.

Entity Framework 6'da her iki yöntem de sonunda aynı dahili yöntemi ToArrayAsync<T>()çağırır , ancak list.ToArray()sonunda çağrılan

T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;

Yani ToArrayAsync<T>()bazı ek yükleri vardır, bu ToListAsync<T>()nedenle tercih edilir.


1
Aslında aradığım cevap buydu, EF bunu nasıl yapıyor. EF Core'da bunun nasıl olduğunu merak ediyorum.
Shimmy Weitzhandler

3

Her zaman eski soru ama yeni soru soran kişiler.

System.Linq.Enumerable kaynağına göre , ToListsadece a new List(source), geri dönmek için ise ToArraya new Buffer<T>(source).ToArray()kullanın T[].

Bellek ayırma hakkında:

IEnumerable<T>Tek bir nesne üzerinde çalışırken, ToArraybelleği bir kez daha ayırın ToList. Ancak çoğu durumda bununla ilgilenmek zorunda değilsiniz, çünkü GC gerektiğinde çöp toplamayı yapacak.

Çalışma zamanı verimli hakkında:

Bu soruyu sorgulayanlar aşağıdaki kodu kendi makinenizde çalıştırabilir ve cevabınızı alırsınız.

class PersonC
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

struct PersonS
{
    public Guid uuid;
    public string name;
    public int age;
    public bool sex;
    public DateTime BirthDay;
    public double weight;
}

class PersonT<T> : IEnumerable<T>
{
    private List<T> items;
    public PersonT(IEnumerable<T> init)
    {
        items = new List<T>(init);
    }

    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => items.GetEnumerator();
}

private IEnumerable<PersonC> C(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonC
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private IEnumerable<PersonS> S(int count)
{
    for (var i = 0; i < count; ++i)
    {
        var guid = Guid.NewGuid();
        var guidBytes = guid.ToByteArray(); //16 bytes
        yield return new PersonS
        {
            uuid = guid,
            name = guid.ToString(),
            age = guidBytes[0] ^ guidBytes[7],
            sex = guidBytes[14] % 2 == 0,
            BirthDay = DateTime.Now.AddDays(-guidBytes[11] * 18),
            weight = guidBytes[12] * 100
        };
    }
}

private void MakeLog(string test, List<long> log) =>
    Console.WriteLine("{0} {1} ms -> [{2}]",
        test,
        log.Average(),
        string.Join(", ", log)
    );

private void Test1(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    MakeLog("C.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = C(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = S(count).ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test2(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC1 = new PersonT<PersonC>(C(count));
    var dataS1 = new PersonT<PersonS>(S(count));

    MakeLog("C1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S1.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS1.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void Test3(int times, int count)
{
    var test = Enumerable.Range(1, times).ToArray();

    var dataC2 = (ICollection<PersonC>) new List<PersonC>(C(count));
    var dataS2 = (ICollection<PersonS>) new List<PersonS>(S(count));

    MakeLog("C2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("C2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataC2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToList", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToList();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());

    MakeLog("S2.ToArray", test.Select(o =>
    {
        var sw = new Stopwatch();
        GC.Collect();
        sw.Start();
        var ret = dataS2.ToArray();
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }).ToList());
}

private void TestMain()
{
    const int times = 100;
    const int count = 1_000_000 + 1;
    Test1(times, count);
    Test2(times, count);
    Test3(times, count);
}

Bu sonuçları makinemde aldım:

Grup 1:

C.ToList 761.79 ms -> [775, 755, 759, 759, 756, 759, 765, 750, 757, 762, 759, 754, 757, 753, 763, 753, 759, 756, 768, 754, 763, 757, 757, 777, 780, 758, 754, 758, 762, 754, 758, 757, 763, 758, 760, 754, 761, 755, 764, 847, 952, 755, 747, 763, 760, 758, 754, 763, 761, 758, 750, 764, 757, 763, 762, 756, 753, 759, 759, 757, 758, 779, 765, 760, 760, 756, 760, 756, 755, 764, 759, 753, 757, 760, 752, 764, 758, 760, 758, 760, 755, 761, 751, 753, 761, 762, 761, 758, 759, 752, 765, 756, 760, 755, 757, 753, 760, 751, 755, 779]
C.ToArray 782.56 ms -> [783, 774, 771, 771, 773, 774, 775, 775, 772, 770, 771, 774, 771, 1023, 975, 772, 767, 776, 771, 779, 772, 779, 775, 771, 775, 773, 775, 771, 765, 774, 770, 781, 772, 771, 781, 762, 817, 770, 775, 779, 769, 774, 763, 775, 777, 769, 777, 772, 775, 778, 775, 771, 770, 774, 772, 769, 772, 769, 774, 775, 768, 775, 769, 774, 771, 776, 774, 773, 778, 769, 778, 767, 770, 787, 783, 779, 771, 768, 805, 780, 779, 767, 773, 771, 773, 785, 1044, 853, 775, 774, 775, 771, 770, 769, 770, 776, 770, 780, 821, 770]
S.ToList 704.2 ms -> [687, 702, 709, 691, 694, 710, 696, 698, 700, 694, 701, 719, 706, 694, 702, 699, 699, 703, 704, 701, 703, 705, 697, 707, 691, 697, 707, 692, 721, 698, 695, 700, 704, 700, 701, 710, 700, 705, 697, 711, 694, 700, 695, 698, 701, 692, 696, 702, 690, 699, 708, 700, 703, 714, 701, 697, 700, 699, 694, 701, 697, 696, 699, 694, 709, 1068, 690, 706, 699, 699, 695, 708, 695, 704, 704, 700, 695, 704, 695, 696, 702, 700, 710, 708, 693, 697, 702, 694, 700, 706, 699, 695, 706, 714, 704, 700, 695, 697, 707, 704]
S.ToArray 742.5 ms -> [742, 743, 733, 745, 741, 724, 738, 745, 728, 732, 740, 727, 739, 740, 726, 744, 758, 732, 744, 745, 730, 739, 738, 723, 745, 757, 729, 741, 736, 724, 744, 756, 739, 766, 737, 725, 741, 742, 736, 748, 742, 721, 746, 1043, 806, 747, 731, 727, 742, 742, 726, 738, 746, 727, 739, 743, 730, 744, 753, 741, 739, 746, 728, 740, 744, 734, 734, 738, 731, 747, 736, 731, 765, 735, 726, 740, 743, 730, 746, 742, 725, 731, 757, 734, 738, 741, 732, 747, 744, 721, 742, 741, 727, 745, 740, 730, 747, 760, 737, 740]

C1.ToList 32.34 ms -> [35, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 32, 31, 31, 31, 31, 30, 32, 31, 31, 31, 31, 32, 30, 31, 31, 31, 30, 32, 31, 31, 31, 36, 31, 31, 31, 32, 30, 31, 32, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 33, 32, 31, 32, 31, 31, 33, 31, 31, 31, 31, 31, 32, 31, 32, 31, 34, 38, 68, 42, 79, 33, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 31, 31, 31, 32, 31, 32, 31, 31, 31, 32, 33, 33, 31, 31]
C1.ToArray 56.32 ms -> [57, 56, 59, 54, 54, 55, 56, 57, 54, 54, 55, 55, 57, 56, 59, 57, 56, 58, 56, 56, 54, 56, 57, 55, 55, 55, 57, 58, 57, 58, 55, 55, 56, 55, 57, 56, 56, 59, 56, 56, 56, 56, 58, 56, 57, 56, 56, 57, 56, 55, 56, 56, 56, 59, 56, 56, 56, 55, 55, 54, 55, 54, 57, 56, 56, 56, 55, 55, 56, 56, 56, 59, 56, 56, 57, 56, 57, 56, 56, 56, 56, 62, 55, 56, 56, 56, 69, 57, 58, 56, 57, 58, 56, 57, 56, 56, 56, 56, 56, 56]
S1.ToList 88.69 ms -> [96, 90, 90, 89, 91, 88, 89, 90, 96, 89, 89, 89, 90, 90, 90, 89, 90, 90, 89, 90, 89, 91, 89, 91, 89, 91, 89, 90, 90, 89, 87, 88, 87, 88, 87, 87, 87, 87, 88, 88, 87, 87, 89, 87, 87, 87, 91, 88, 87, 86, 89, 87, 90, 89, 89, 90, 89, 87, 87, 87, 86, 87, 88, 90, 88, 87, 87, 92, 87, 87, 88, 88, 88, 86, 86, 87, 88, 87, 87, 87, 89, 87, 89, 87, 90, 89, 89, 89, 91, 89, 90, 89, 90, 88, 90, 90, 90, 88, 89, 89]
S1.ToArray 143.26 ms -> [130, 129, 130, 131, 133, 130, 131, 130, 135, 137, 130, 136, 132, 131, 130, 131, 132, 130, 132, 136, 130, 131, 157, 153, 194, 364, 176, 189, 203, 194, 189, 192, 183, 140, 142, 147, 145, 134, 159, 158, 142, 167, 130, 143, 145, 144, 160, 154, 156, 153, 153, 164, 142, 145, 137, 134, 145, 143, 142, 135, 133, 133, 135, 134, 134, 139, 139, 133, 134, 141, 133, 132, 133, 132, 133, 131, 135, 132, 133, 132, 128, 128, 130, 132, 129, 129, 129, 129, 129, 128, 134, 129, 129, 129, 129, 128, 128, 137, 130, 131]

C2.ToList 3.25 ms -> [5, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3]
C2.ToArray 3.37 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 5, 4, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 4, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 4, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3]
S2.ToList 37.72 ms -> [38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 40, 38, 38, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 39, 37, 37, 39, 38, 37, 37, 37, 37, 39, 38, 37, 37, 38, 37, 38, 37, 37, 38, 37, 37, 37, 38, 37, 37, 36, 37, 38, 37, 39, 37, 39, 38, 37, 38, 38, 38, 38, 38, 38, 37, 38, 38, 38, 38, 38, 37, 38, 37, 37, 38, 37, 37, 39, 41, 37, 38, 38, 37, 37, 37, 37, 38, 37, 37, 37, 40, 37, 37, 37, 37, 39, 38]
S2.ToArray 38.86 ms -> [39, 37, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 38, 38, 38, 39, 37, 38, 38, 38, 38, 38, 37, 37, 38, 37, 37, 38, 38, 40, 38, 38, 38, 38, 38, 39, 38, 38, 39, 38, 38, 39, 38, 38, 40, 38, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 39, 37, 38, 38, 39, 71, 78, 37, 37, 37, 39, 38, 38, 39, 38, 38, 38, 38, 38, 39, 38, 38, 38, 39, 38, 38, 38]

Grubu2:

C.ToList 756.81 ms
C.ToArray 774.21 ms
S.ToList 709.7 ms
S.ToArray 753.51 ms

C1.ToList 32.06 ms
C1.ToArray 56.58 ms
S1.ToList 89.43 ms
S1.ToArray 132.85 ms

C2.ToList 3.45 ms
C2.ToArray 3.36 ms
S2.ToList 41.43 ms
S2.ToArray 40.84 ms

Grubu3:

C.ToList 756.64 ms
C.ToArray 771.56 ms
S.ToList 705.42 ms
S.ToArray 749.59 ms

C1.ToList 31.45 ms
C1.ToArray 57.03 ms
S1.ToList 91.26 ms
S1.ToArray 129.77 ms

C2.ToList 3.26 ms
C2.ToArray 3.29 ms
S2.ToList 41.57 ms
S2.ToArray 40.69 ms

Group4:

C.ToList 729.65 ms -> [749, 730, 721, 719, 723, 743, 721, 724, 727, 722, 716, 725, 723, 726, 718, 722, 731, 722, 723, 725, 723, 722, 728, 726, 728, 718, 726, 1088, 788, 737, 729, 710, 730, 728, 717, 723, 728, 721, 722, 728, 722, 736, 723, 729, 732, 724, 726, 727, 728, 728, 726, 726, 725, 727, 725, 728, 728, 718, 724, 725, 726, 724, 726, 729, 727, 722, 722, 725, 725, 728, 724, 727, 738, 717, 726, 723, 725, 725, 727, 724, 720, 726, 726, 723, 727, 730, 723, 721, 725, 727, 727, 733, 720, 722, 722, 725, 722, 725, 728, 726]
C.ToArray 788.36 ms -> [748, 740, 742, 797, 1090, 774, 781, 787, 784, 786, 786, 782, 781, 781, 784, 783, 783, 781, 783, 787, 783, 784, 775, 789, 784, 785, 778, 774, 781, 783, 786, 781, 780, 788, 778, 785, 777, 781, 786, 782, 781, 787, 782, 787, 784, 773, 783, 782, 781, 777, 783, 781, 785, 788, 777, 776, 784, 784, 783, 789, 778, 781, 791, 768, 779, 783, 781, 787, 786, 781, 784, 781, 785, 781, 780, 809, 1155, 780, 790, 789, 783, 776, 785, 783, 786, 787, 782, 782, 787, 777, 779, 784, 783, 776, 786, 775, 782, 779, 784, 784]
S.ToList 705.54 ms -> [690, 705, 709, 708, 702, 707, 703, 696, 703, 702, 700, 703, 700, 707, 705, 699, 697, 703, 695, 698, 707, 697, 711, 710, 699, 700, 708, 707, 693, 710, 704, 691, 702, 700, 703, 700, 705, 700, 703, 695, 709, 705, 698, 699, 709, 700, 699, 704, 691, 705, 703, 700, 708, 1048, 710, 706, 706, 692, 702, 705, 695, 701, 710, 697, 698, 706, 705, 707, 707, 695, 698, 704, 698, 699, 705, 698, 703, 702, 701, 697, 702, 702, 704, 703, 699, 707, 703, 705, 701, 717, 698, 695, 713, 696, 708, 705, 697, 699, 700, 698]
S.ToArray 745.01 ms -> [751, 743, 727, 734, 736, 745, 739, 750, 739, 750, 758, 739, 744, 738, 730, 744, 745, 739, 744, 750, 733, 735, 743, 731, 749, 748, 727, 746, 749, 731, 737, 803, 1059, 756, 769, 748, 740, 745, 741, 746, 749, 732, 741, 742, 732, 744, 746, 737, 742, 739, 733, 744, 741, 729, 746, 760, 725, 741, 764, 739, 750, 751, 727, 745, 738, 727, 735, 741, 720, 736, 740, 733, 741, 746, 731, 749, 756, 740, 738, 736, 732, 741, 741, 733, 741, 744, 736, 742, 742, 735, 743, 746, 729, 748, 765, 743, 734, 742, 728, 749]

C1.ToList 32.27 ms -> [36, 31, 31, 32, 31, 32, 31, 30, 32, 30, 30, 30, 34, 32, 31, 31, 31, 31, 31, 31, 31, 32, 38, 51, 68, 57, 35, 30, 31, 31, 30, 30, 33, 30, 31, 34, 31, 34, 32, 31, 31, 31, 31, 32, 30, 30, 31, 30, 31, 31, 32, 31, 31, 31, 32, 31, 31, 31, 32, 31, 33, 31, 31, 32, 30, 30, 30, 30, 30, 33, 30, 33, 32, 31, 30, 31, 31, 32, 32, 31, 35, 31, 34, 31, 31, 32, 31, 31, 32, 31, 32, 31, 31, 35, 31, 31, 31, 31, 31, 32]
C1.ToArray 56.72 ms -> [58, 56, 57, 57, 59, 58, 58, 57, 56, 59, 57, 55, 55, 54, 56, 55, 56, 56, 57, 59, 56, 55, 58, 56, 55, 55, 55, 55, 58, 58, 55, 57, 57, 56, 57, 57, 57, 57, 59, 59, 56, 57, 56, 57, 57, 56, 57, 59, 58, 56, 57, 57, 57, 58, 56, 56, 59, 56, 59, 57, 57, 57, 57, 59, 57, 56, 57, 56, 58, 56, 57, 56, 57, 59, 55, 58, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 56, 56, 57, 56, 56, 57, 58, 57, 57, 57, 57, 57]
S1.ToList 90.72 ms -> [95, 90, 90, 89, 89, 89, 91, 89, 89, 87, 91, 89, 89, 89, 91, 89, 89, 89, 90, 89, 89, 90, 88, 89, 88, 90, 89, 90, 89, 89, 90, 90, 89, 89, 90, 91, 89, 91, 89, 90, 89, 89, 90, 91, 89, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 89, 90, 89, 91, 89, 90, 89, 90, 89, 90, 89, 96, 89, 90, 89, 89, 89, 89, 89, 90, 89, 89, 89, 90, 87, 89, 90, 90, 91, 89, 91, 89, 89, 90, 91, 90, 89, 93, 144, 149, 90, 90, 89, 89, 89]
S1.ToArray 131.4 ms -> [130, 128, 127, 134, 129, 129, 130, 136, 131, 130, 132, 132, 133, 131, 132, 131, 133, 132, 130, 131, 132, 131, 130, 133, 133, 130, 130, 131, 131, 131, 132, 134, 131, 131, 132, 131, 132, 131, 134, 131, 131, 130, 131, 131, 130, 132, 129, 131, 131, 131, 132, 131, 133, 134, 131, 131, 132, 132, 131, 133, 131, 131, 130, 133, 131, 130, 134, 132, 131, 132, 132, 131, 131, 134, 131, 131, 132, 132, 131, 130, 138, 130, 130, 131, 132, 132, 130, 134, 131, 131, 132, 131, 130, 132, 133, 131, 131, 131, 130, 131]

C2.ToList 3.21 ms -> [4, 3, 3, 3, 4, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3]
C2.ToArray 3.22 ms -> [4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4]
S2.ToList 41.46 ms -> [42, 40, 41, 40, 42, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 39, 41, 41, 39, 40, 40, 43, 40, 39, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 43, 40, 43, 75, 76, 47, 39, 40, 40, 40, 40, 42, 40, 41, 40, 40, 40, 44, 41, 40, 42, 42, 40, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 42, 41, 40, 41, 41, 42, 42, 41, 40, 41, 41, 41, 41, 41, 40, 42, 40, 42, 41, 41, 41, 43, 41, 41, 41, 41, 42, 41]
S2.ToArray 41.14 ms -> [42, 41, 41, 40, 40, 40, 40, 41, 41, 42, 41, 42, 41, 41, 41, 42, 41, 41, 42, 41, 41, 41, 41, 41, 42, 40, 41, 40, 42, 40, 42, 41, 40, 42, 41, 41, 43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 41, 41, 41, 40, 42, 41, 41, 41, 41, 41, 40, 41, 41, 42, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, 42, 42, 42, 41, 45, 46, 41, 40, 41, 41, 42, 41, 41, 41, 41, 41, 41, 40, 41, 43, 40, 40, 40, 40, 43, 41]

Grup5:

C.ToList 757.06 ms -> [770, 752, 752, 751, 778, 763, 761, 763, 747, 758, 748, 747, 754, 749, 752, 753, 756, 762, 750, 753, 756, 749, 755, 757, 755, 756, 755, 744, 753, 758, 747, 751, 759, 751, 761, 755, 746, 752, 752, 749, 746, 752, 753, 755, 752, 755, 754, 754, 966, 937, 749, 759, 748, 747, 754, 749, 755, 750, 746, 754, 757, 752, 753, 745, 758, 755, 761, 753, 751, 755, 755, 752, 746, 756, 755, 746, 742, 751, 751, 749, 752, 751, 756, 756, 755, 742, 749, 754, 749, 756, 753, 751, 754, 752, 751, 754, 753, 749, 755, 756]
C.ToArray 772.8 ms -> [766, 772, 755, 763, 758, 767, 763, 762, 761, 768, 769, 763, 770, 757, 765, 760, 766, 759, 764, 761, 760, 777, 1102, 881, 759, 765, 758, 762, 772, 761, 758, 757, 765, 769, 769, 761, 762, 762, 763, 760, 770, 764, 760, 768, 758, 766, 763, 770, 769, 761, 764, 761, 761, 767, 761, 762, 764, 757, 765, 766, 767, 771, 753, 762, 769, 768, 759, 764, 764, 760, 763, 763, 763, 763, 763, 767, 761, 771, 760, 765, 760, 758, 768, 770, 751, 771, 767, 771, 765, 763, 760, 765, 765, 769, 767, 767, 1193, 774, 767, 764]
S.ToList 704.73 ms -> [682, 708, 705, 699, 705, 704, 695, 703, 702, 699, 701, 708, 699, 702, 703, 701, 701, 699, 701, 707, 707, 700, 701, 705, 700, 697, 706, 702, 701, 706, 699, 692, 702, 697, 707, 704, 697, 698, 699, 699, 702, 703, 698, 697, 702, 703, 702, 704, 694, 697, 707, 695, 711, 710, 700, 693, 703, 699, 699, 706, 698, 701, 703, 704, 698, 706, 700, 704, 701, 699, 702, 705, 694, 698, 709, 736, 1053, 704, 694, 700, 698, 696, 701, 700, 700, 706, 706, 692, 698, 707, 703, 695, 703, 699, 694, 708, 695, 694, 706, 695]
S.ToArray 744.17 ms -> [746, 740, 725, 740, 739, 731, 746, 760, 735, 738, 740, 734, 744, 748, 737, 744, 745, 727, 736, 738, 728, 743, 745, 735, 748, 760, 739, 748, 762, 742, 741, 747, 733, 746, 758, 742, 742, 741, 724, 744, 747, 727, 740, 740, 729, 742, 757, 741, 740, 742, 726, 739, 746, 1133, 749, 737, 730, 740, 747, 733, 747, 752, 731, 747, 742, 730, 741, 749, 731, 749, 743, 730, 747, 742, 731, 737, 745, 734, 739, 735, 727, 743, 752, 731, 744, 742, 729, 740, 746, 731, 739, 746, 733, 745, 743, 733, 739, 742, 727, 737]

C1.ToList 31.71 ms -> [35, 32, 32, 30, 31, 33, 31, 32, 32, 31, 31, 32, 32, 33, 32, 31, 31, 32, 31, 32, 32, 32, 31, 32, 33, 32, 31, 31, 31, 32, 31, 34, 31, 31, 32, 33, 32, 32, 31, 32, 34, 32, 31, 32, 33, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 31, 33, 30, 31, 32, 30, 30, 33, 32, 32, 34, 31, 31, 31, 31, 32, 31, 31, 31, 31, 32, 31, 31, 33, 31, 32, 32, 32, 33, 32, 31, 31, 31, 31, 31, 32, 32, 33, 32, 31, 31, 32]
C1.ToArray 59.53 ms -> [63, 57, 58, 58, 57, 59, 59, 57, 60, 131, 127, 67, 58, 56, 59, 56, 57, 58, 58, 58, 57, 59, 60, 57, 57, 59, 60, 57, 57, 57, 58, 58, 58, 58, 57, 57, 61, 57, 58, 57, 57, 57, 57, 57, 58, 58, 58, 58, 57, 58, 59, 57, 58, 57, 57, 59, 58, 58, 59, 57, 59, 57, 56, 56, 59, 56, 56, 59, 57, 58, 58, 58, 57, 58, 59, 59, 58, 57, 58, 62, 65, 57, 57, 57, 58, 60, 59, 58, 59, 57, 58, 57, 58, 59, 58, 58, 58, 59, 60, 58]
S1.ToList 82.78 ms -> [87, 82, 83, 83, 82, 82, 83, 84, 82, 83, 84, 84, 84, 82, 82, 84, 82, 84, 83, 84, 82, 82, 82, 81, 83, 83, 83, 84, 84, 82, 82, 83, 83, 83, 82, 83, 85, 83, 82, 82, 84, 82, 82, 83, 83, 83, 82, 82, 82, 83, 82, 83, 82, 84, 82, 83, 82, 83, 82, 82, 82, 84, 82, 83, 82, 82, 86, 83, 83, 82, 83, 83, 83, 82, 84, 82, 83, 81, 82, 82, 82, 82, 83, 83, 83, 82, 83, 84, 83, 82, 83, 83, 83, 82, 83, 84, 82, 82, 83, 83]
S1.ToArray 122.3 ms -> [122, 119, 119, 120, 119, 120, 120, 121, 119, 119, 122, 120, 120, 120, 122, 120, 123, 120, 120, 120, 121, 123, 120, 120, 120, 121, 120, 121, 122, 120, 123, 119, 121, 118, 121, 120, 120, 120, 119, 124, 119, 121, 119, 120, 120, 120, 120, 120, 122, 121, 123, 230, 203, 123, 119, 119, 122, 119, 120, 120, 120, 122, 120, 121, 120, 121, 120, 121, 120, 121, 120, 120, 120, 121, 122, 121, 123, 119, 119, 119, 119, 121, 120, 120, 120, 122, 121, 122, 119, 120, 120, 121, 121, 120, 121, 120, 121, 118, 118, 118]

C2.ToList 3.43 ms -> [5, 3, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 6, 4, 4, 3, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 3, 3, 3]
C2.ToArray 3.48 ms -> [3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 3]
S2.ToList 41.47 ms -> [41, 41, 49, 67, 82, 41, 41, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 41, 40, 42, 42, 40, 40, 41, 41, 41, 40, 41, 40, 41, 40, 41, 40, 42, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 41, 41, 41, 42, 40, 41, 40, 40, 40, 42, 40, 41, 42, 41, 42, 41, 42, 40, 41, 41, 41, 41, 41, 41, 41, 41, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 40, 41, 41, 41, 41, 41, 43, 40, 40, 41, 42, 41]
S2.ToArray 40.62 ms -> [42, 41, 44, 40, 40, 40, 40, 41, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 41, 40, 41, 40, 40, 41, 42, 41, 41, 41, 40, 40, 40, 40, 40, 41, 41, 42, 40, 41, 41, 41, 41, 41, 40, 42, 40, 40, 41, 41, 41, 40, 41, 40, 40, 40, 40, 40, 41, 40, 40, 41, 40, 40, 40, 40, 41, 40, 41, 41, 41, 40, 41, 41, 40, 41, 40, 41, 42, 40, 41, 41, 42, 41, 41, 40, 41, 40, 41, 40, 41, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40]

Stackoverflow'un cevabın karakter miktarı sınırlaması nedeniyle, Grup2 ve Grup3'ün örnek listeleri atlanır.

Gördüğünüz gibi , çoğu durumda ToListveya kullanmak gerçekten önemli değil ToArry.

Çalışma zamanı tarafından hesaplanan IEnumerable<T>nesneleri işlerken , hesaplamanın getirdiği yük, bellek ayırma ve kopyalama işlemlerinden daha ağırsa ToListve ToArrayeşitsizlik önemsizdir ( C.ToList vs C.ToArrayve S.ToList vs S.ToArray).

Fark sadece çalışma zamanı hesaplanmamış IEnumerable<T>yalnızca nesnelerde ( C1.ToList vs C1.ToArrayve S1.ToList vs S1.ToArray) gözlemlenebilir . Ancak mutlak fark (<60ms) bir milyon küçük nesnede hala kabul edilebilir IEnumerable<T>. Aslında, fark uygulanmasıyla belirlenir Enumerator<T>arasında IEnumerable<T>. Yani, programınız gerçekten bu konuda gerçekten hassassa, profil, profil, profil gerekir ! Sonunda muhtemelen darboğazın açık olmadığını ToListveya ToArraynumaralandırıcıların detayını bulacaksınız .

Ve sonucu gösterir C2.ToList vs C2.ToArrayve S2.ToList vs S2.ToArraygerçekten, bakım gerektirmeyen ToListveya ToArrayçalışma zamanı hesaplanmamışICollection<T> nesneler.

Tabii ki, bu sadece makinemdeki sonuçlar, bu işlemlerin farklı makinelerde gerçek zaman harcaması aynı olmayacak, yukarıdaki kodu kullanarak makinenizde bulabilirsiniz.

Bir seçim yapmanızın tek nedeni , @Jeppe Stig Nielsen'in cevabında açıklandığı gibi List<T>veya özel ihtiyaçlarınız olmasıdır .T[]


1

Bu sonucu başka bir Linq-sql'de kullanmak isteyen herkes için

from q in context.MyTable
where myListOrArray.Contains(q.someID)
select q;

myListOrArray için bir Liste veya Dizi kullansanız da, oluşturulan SQL aynıdır. Şimdi bazı bile neden bu ifadeden önce numaralandırma sorabilir biliyorum, ama IQueryable vs (Liste veya Dizi) oluşturulan SQL arasında bir fark var.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.