Apakah lebih baik untuk memanggil ToList () atau ToArray () di kueri LINQ?

519

Saya sering mengalami kasus di mana saya ingin membuat kueri tepat di tempat saya menyatakannya. Ini biasanya karena saya perlu mengulanginya beberapa kali dan itu mahal untuk dihitung. Sebagai contoh:

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

Ini berfungsi dengan baik. Tetapi jika saya tidak akan mengubah hasilnya, maka saya mungkin akan menelepon ToArray()saja ToList().

Namun saya bertanya-tanya apakah ToArray()diimplementasikan oleh panggilan pertama ToList()dan karena itu memori kurang efisien daripada hanya menelepon ToList().

Apakah saya gila? Haruskah saya menelepon ToArray()- aman dan aman karena mengetahui bahwa memori tidak akan dialokasikan dua kali?

Frank Krueger
sumber
10
Jika Anda pernah ingin mengetahui apa yang terjadi di balik tirai di NET, saya benar-benar merekomendasikan NET Reflector
David Hedlund
32
@ Davidvided Saya merekomendasikan kode sumber bersih .
Gqqnbig
1
Saya tidak setuju bahwa stackoverflow.com/questions/6750447/c-toarray-performance adalah duplikat dari pertanyaan ini meskipun ada hubungan yang penting. Baik penggunaan memori (pertanyaan ini) dan kinerja (pertanyaan lain) dan merupakan pertimbangan yang menarik dan tidak pribadi. Mereka dapat dideskripsikan secara terpisah, tetapi keduanya harus menjadi faktor dalam keputusan untuk memilih satu dari yang lain. Saya tidak bisa merekomendasikan salah satu jawaban untuk ini atau pertanyaan lain sebagai komprehensif. Ada beberapa jawaban yang ketika diambil bersama-sama memberikan diskusi yang agak lengkap tentang bagaimana memilih satu dari yang lain.
steve
1
@Gqqnbig - komentar paling berguna yang pernah ada! Terima kasih :-)
Mark Cooper

Jawaban:

366

Kecuali Anda hanya membutuhkan array untuk memenuhi kendala lain yang harus Anda gunakan ToList. Pada sebagian besar skenario ToArrayakan mengalokasikan lebih banyak memori daripada ToList.

Keduanya menggunakan array untuk penyimpanan, tetapi ToListmemiliki kendala yang lebih fleksibel. Perlu array setidaknya sebesar jumlah elemen dalam koleksi. Jika array lebih besar, itu tidak masalah. Namun ToArrayperlu ukuran array yang tepat dengan jumlah elemen.

Untuk memenuhi kendala ini ToArraysering melakukan satu alokasi lebih banyak daripada ToList. Setelah memiliki array yang cukup besar itu mengalokasikan array yang ukurannya tepat dan menyalin elemen kembali ke array itu. Satu-satunya waktu yang dapat dihindarkan adalah ketika algoritma tumbuh untuk array kebetulan bertepatan dengan jumlah elemen yang perlu disimpan (pasti dalam minoritas).

EDIT

Beberapa orang bertanya kepada saya tentang konsekuensi memiliki memori ekstra yang tidak digunakan dalam List<T>nilai.

Ini adalah kekhawatiran yang valid. Jika koleksi yang dibuat berumur panjang, tidak pernah dimodifikasi setelah dibuat dan memiliki peluang besar untuk mendarat di tumpukan Gen2 maka Anda mungkin lebih baik mengambil alokasi ekstra ToArraydi muka.

Secara umum, meskipun saya menemukan ini menjadi kasus yang lebih jarang. Jauh lebih umum untuk melihat banyak ToArraypanggilan yang segera diteruskan ke penggunaan memori jangka pendek lainnya dalam hal ToListini terbukti lebih baik.

Kuncinya di sini adalah profil, profil dan kemudian profil lagi.

JaredPar
sumber
14
Di sisi lain, bukankah memori tambahan yang dialokasikan untuk kerja keras menciptakan array memenuhi syarat untuk pengumpulan sampah, sedangkan overhead tambahan untuk Daftar akan tetap? Saya katakan lebih sederhana. Jika Anda perlu menambah atau menghapus elemen, ada alat untuk itu. Jika tidak, ada alat lain untuk itu. Gunakan yang masuk akal. Jika nanti, Anda menemukan masalah dengan memori dan kinerja, dan ini dia , ubahlah.
Anthony Pegram
1
@AnthonyPegram ya itu adalah pertimbangan yang sah untuk dibuat. Jika nilai tersebut digunakan dalam penyimpanan jangka panjang, tidak akan dimodifikasi, dan berpotensi membuatnya menjadi Gen 2 maka Anda mungkin lebih baik membayar alokasi tambahan sekarang dibandingkan mencemari tumpukan Gen 2. Meskipun saya jarang melihat ini. Jauh lebih umum untuk melihat ToArray diteruskan langsung ke kueri LINQ lain yang berumur pendek.
JaredPar
2
@AnthonyPegram saya memperbarui jawaban saya untuk memasukkan sisi diskusi ini
JaredPar
8
@ JaredPar Saya tidak mengerti bagaimana ToArraybisa mengalokasikan lebih banyak memori jika perlu ukuran lokasi yang tepat di mana ToList<>jelas memiliki lokasi cadangan otomatis itu. (peningkatan otomatis)
Royi Namir
5
@RoyiNamir karena ToArray pertama-tama melakukan alokasi gaya-ToList dengan overhead, kemudian melakukan alokasi ukuran persis tambahan.
Timbo
169

Perbedaan kinerja tidak akan signifikan, karena List<T>diimplementasikan sebagai array berukuran dinamis. Memanggil salah satu ToArray()(yang menggunakan Buffer<T>kelas internal untuk menumbuhkan array) atau ToList()(yang memanggil List<T>(IEnumerable<T>)konstruktor) akhirnya akan menjadi masalah menempatkan mereka ke dalam array dan menumbuhkan array sampai cocok dengan mereka semua.

Jika Anda menginginkan konfirmasi konkret atas fakta ini, periksa penerapan metode yang dimaksud dalam Reflektor - Anda akan melihatnya menerapkannya ke kode yang hampir sama.

mqp
sumber
2
Fakta menarik yang saya temui adalah bahwa untuk kueri berkorelasi yang disebabkan oleh penggunaan grup yang ditentukan melalui grup yang tergabung dalam proyeksi Anda menyebabkan Linq ke SQL untuk menambahkan sub-kueri lain untuk mengambil hitungan untuk grup itu. Saya berasumsi bahwa ini berarti dalam kasus ini ukuran koleksi akan diketahui sebelum item diambil dan dengan demikian array ukuran yang tepat dapat dibuat secara langsung yang akan menghemat sumber daya pemrosesan dan memori sambil mewujudkan hasil.
jpierson
133
Jika Hitungan diketahui sebelumnya, kinerjanya identik. Namun, jika Hitungan tidak diketahui sebelumnya, satu-satunya perbedaan antara ToArray()dan ToList()adalah bahwa yang pertama harus memangkas kelebihan, yang melibatkan menyalin seluruh array, sedangkan yang terakhir tidak memangkas kelebihan, tetapi menggunakan rata-rata 25 % lebih banyak memori. Ini hanya akan memiliki implikasi jika tipe datanya besar struct. Hanya makanan untuk dipikirkan.
Scott Rippey
9
@EldritchConundrum 25% berasal dari logika ini: Jika jumlah item tidak diketahui, maka panggilan ToListatau ToArrayakan mulai dengan membuat buffer kecil. Ketika buffer itu terisi, itu menggandakan kapasitas buffer dan berlanjut. Karena kapasitas selalu berlipat ganda, buffer yang tidak digunakan akan selalu berada di antara 0% dan 50%.
Scott Rippey
2
@ScottRippey Saya baru saja mencari sumber Daftar baru dari sumber IEnumerable, dan memeriksa apakah IEnumerable adalah ICollection, dan jika ya, maka ia mulai dengan mengalokasikan satu array dengan ukuran persis yang dibutuhkan dari properti Count, jadi ini akan menjadi kasus di mana ToList () pasti akan lebih cepat. Jawaban lengkap dapat mencakup fakta itu, meskipun saya tidak berpikir itu adalah kasus yang paling umum.
AndyClaw
3
@AndyClaw Keduanya Listdan Bufferakan memeriksa ICollection, dalam hal ini kinerja akan identik.
Scott Rippey
54

(tujuh tahun kemudian ...)

Beberapa jawaban (baik) lainnya telah berkonsentrasi pada perbedaan kinerja mikroskopis yang akan terjadi.

Posting ini hanyalah suplemen untuk menyebutkan perbedaan semantik yang ada antara yang IEnumerator<T>diproduksi oleh array ( T[]) dibandingkan dengan yang dikembalikan oleh a List<T>.

Terbaik diilustrasikan dengan contoh:

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

Kode di atas akan berjalan tanpa kecuali dan menghasilkan output:

1
2
3
4
5
6
7
8
900
10

Ini menunjukkan bahwa yang IEnumarator<int>dikembalikan oleh suatu int[]tidak melacak apakah array telah dimodifikasi sejak pembuatan enumerator.

Perhatikan bahwa saya mendeklarasikan variabel lokal sourcesebagai IList<int>. Dengan cara itu saya memastikan compiler C # tidak mengoptimalkan foreachpernyataan menjadi sesuatu yang setara dengan for (var idx = 0; idx < source.Length; idx++) { /* ... */ }loop. Ini adalah sesuatu yang mungkin dilakukan oleh kompiler C jika saya menggunakannya var source = ...;. Dalam versi saya saat ini. NET framework enumerator yang sebenarnya digunakan di sini adalah tipe referensi non-publik System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]tapi tentu saja ini adalah detail implementasi.

Sekarang, jika saya mengubah .ToArray()menjadi .ToList(), saya hanya mendapatkan:

1
2
3
4
5

diikuti dengan System.InvalidOperationExceptionsemburan yang mengatakan:

Koleksi telah dimodifikasi; operasi penghitungan tidak dapat dijalankan.

Pencacah yang mendasari dalam kasus ini adalah tipe nilai publik yang dapat berubah System.Collections.Generic.List`1+Enumerator[System.Int32](kotak di dalam IEnumerator<int>kotak dalam kasus ini karena saya gunakan IList<int>).

Kesimpulannya, enumerator yang diproduksi olehList<T>melacak apakah daftar berubah selama enumerasi, sedangkan enumerator yang dihasilkan olehT[]tidak. Jadi pertimbangkan perbedaan ini ketika memilih antara.ToList()dan.ToArray().

Orang sering menambahkan satu ekstra .ToArray() atau .ToList()mengelak dari koleksi yang melacak apakah itu dimodifikasi selama masa pencacah.

(Jika ada yang ingin tahu bagaimana yang List<>melacak apakah koleksi dimodifikasi, ada bidang swasta _versiondi kelas ini yang berubah setiap kali yang List<>diperbarui.)

Jeppe Stig Nielsen
sumber
28

Saya setuju dengan @mquander bahwa perbedaan kinerja tidak signifikan. Namun, saya ingin membuat tolok ukur untuk memastikan, jadi saya lakukan - dan itu, tidak signifikan.

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

Setiap larik / Daftar sumber memiliki 1000 elemen. Jadi, Anda dapat melihat bahwa perbedaan waktu dan memori dapat diabaikan.

Kesimpulan saya: Anda mungkin juga menggunakan ToList () , karena a List<T>menyediakan lebih banyak fungsionalitas daripada sebuah array, kecuali beberapa byte memori benar-benar penting bagi Anda.

EMP
sumber
1
Saya ingin tahu apakah hasil ini akan berbeda jika Anda menggunakan yang besar dan structbukan tipe atau kelas primitif.
Scott Rippey
12
Daftar <T> .ToList ???? Arti apa? Anda sebaiknya mencoba memberikan IEnumerable ke dalamnya, yang tidak mengimplementasikan antarmuka ICollection.
Grigory
8
Saya ingin memastikan bahwa saya hanya mengukur waktu ToListatau ToArraypanggilan dan bukan penghitungan IEnumerable. Daftar <T> .ToList () masih membuat Daftar <T> baru - tidak hanya "mengembalikan ini".
EMP
23
-1 Perilaku ToArray()dan ToList()terlalu banyak perbedaan ketika diberikan ICollection<T>parameter - Mereka hanya melakukan alokasi tunggal dan operasi salinan tunggal. Keduanya List<T>dan Arrayimplementasikan ICollection<T>, sehingga tolok ukur Anda tidak valid sama sekali.
Mohammad Dehghan
1
Bagi siapa pun yang tertarik, saya memposting tolok ukur saya sendiri sebagai jawaban terpisah . Ini digunakan .Select(i => i)untuk menghindari ICollection<T>masalah implementasi, dan termasuk kelompok kontrol untuk melihat berapa banyak waktu yang diambil iterasi dari sumber IEnumerable<>di tempat pertama.
StriplingWarrior
19

ToList()biasanya lebih disukai jika Anda menggunakannya di IEnumerable<T>(dari ORM, misalnya). Jika panjang urutan tidak diketahui di awal, ToArray()buat koleksi panjang dinamis seperti Daftar dan kemudian mengubahnya menjadi array, yang membutuhkan waktu ekstra.

Vitaliy Ulantikov
sumber
26
Saya telah memutuskan bahwa keterbacaan mengalahkan kinerja dalam hal ini. Saya sekarang hanya menggunakan ToList ketika saya berharap untuk terus menambahkan elemen. Dalam semua kasus lainnya (kebanyakan kasus), saya menggunakan ToArray. Tapi terima kasih atas masukannya!
Frank Krueger
5
Mencari di ILSpy, Enumerable.ToArray()menelepon new Buffer<TSource>(source).ToArray(). Dalam Buffer constructor jika sumber mengimplementasikan ICollection maka ia memanggil source.CopyTo (item, 0), dan kemudian .ToArray () mengembalikan array item internal secara langsung. Jadi tidak ada konversi yang membutuhkan waktu ekstra dalam kasus itu. Jika sumber tidak mengimplementasikan ICollection maka ToArray akan menghasilkan salinan array untuk memangkas lokasi tambahan yang tidak digunakan dari akhir array seperti dijelaskan oleh komentar Scott Rippey di atas.
BrandonAGr
19

Memori akan selalu dialokasikan dua kali - atau sesuatu yang dekat dengan itu. Karena Anda tidak dapat mengubah ukuran array, kedua metode akan menggunakan semacam mekanisme untuk mengumpulkan data dalam koleksi yang berkembang. (Yah, Daftar itu sendiri merupakan koleksi yang terus tumbuh.)

Daftar menggunakan array sebagai penyimpanan internal, dan menggandakan kapasitas saat dibutuhkan. Ini berarti bahwa rata-rata 2/3 dari item telah dialokasikan kembali setidaknya sekali, setengah dari mereka yang dialokasikan kembali setidaknya dua kali, setengah dari mereka setidaknya tiga kali, dan seterusnya. Itu berarti bahwa setiap item rata-rata telah dialokasikan kembali 1,3 kali, yang tidak terlalu banyak biaya overhead.

Ingat juga bahwa jika Anda mengumpulkan string, koleksi itu sendiri hanya berisi referensi ke string, string itu sendiri tidak dialokasikan kembali.

Guffa
sumber
Ini mungkin hal yang bodoh untuk ditanyakan, tetapi bukankah logika 2/3, 1/3, 1/6 yang Anda garis besar berasumsi bahwa array Daftar dapat diperpanjang pada tempatnya? Artinya, ada ruang kosong di akhir array sehingga alokasi yang ada tidak perlu dipindahkan?
@JonofAllTrades: Tidak, array tidak pernah diperluas, manajemen memori dalam. NET tidak melakukan itu. Jika akan diperpanjang di tempat, tidak perlu ada realokasi barang.
Guffa
Ah, saya mengerti: barang-barang yang tidak dialokasikan kembali tidak harus melakukannya karena mereka berada dalam alokasi akhir. Semua item yang dialokasikan dalam alokasi sebelumnya dipindahkan, tetapi karena peningkatan logaritmik dalam panjang array, ini adalah fraksi yang dapat dihitung. Terima kasih telah mengklarifikasi!
19

Ini tahun 2020 di luar dan semua orang menggunakan .NET Core 3.1 jadi saya memutuskan untuk menjalankan beberapa tolok ukur dengan Benchmark.NET.

TL; DR: ToArray () lebih baik dari segi kinerja dan melakukan maksud penyampaian pekerjaan yang lebih baik jika Anda tidak berencana untuk mengubah koleksi.


    [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>();
    }

Hasilnya adalah:


    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)
Tyrrrz
sumber
1
Jika Anda tidak berencana untuk memutasi koleksi, saya pikir maksudnya dapat lebih baik ditunjukkan dengan ToImmutableArray()(dari System.Collections.Paket yang dapat ditentukan) 😉
Arturo Torres Sánchez
@ ArturoTorresSánchez benar, tetapi jika koleksi tidak diekspos di luar metode, saya hanya akan menggunakan array.
Tyrrrz
2
Terima kasih untuk ini. Jawaban yang dipilih adalah argumen belaka dan mengasumsikan hasil mengikuti argumen itu. Untuk melakukan ini secara ilmiah dan sebagai bonus tahu seberapa besar perbedaan yang ada, hanya ada satu cara nyata untuk mengetahuinya.
Jonas
15

Sunting : Bagian terakhir dari jawaban ini tidak valid. Namun, sisanya masih merupakan informasi yang berguna, jadi saya akan meninggalkannya.

Saya tahu ini adalah posting lama, tetapi setelah memiliki pertanyaan yang sama dan melakukan penelitian, saya menemukan sesuatu yang menarik yang mungkin layak untuk dibagikan.

Pertama, saya setuju dengan @mquander dan jawabannya. Dia benar dalam mengatakan bahwa dari segi kinerja, keduanya identik.

Namun, saya telah menggunakan Reflector untuk melihat metode-metode di System.Linq.Enumerablenamespace ekstensi, dan saya perhatikan optimasi yang sangat umum.
Jika memungkinkan, IEnumerable<T>sumber dilemparkan ke IList<T>atau ICollection<T>untuk mengoptimalkan metode. Sebagai contoh, lihat ElementAt(int).

Menariknya, Microsoft memilih untuk hanya mengoptimalkan IList<T>, tetapi tidak IList. Sepertinya Microsoft lebih suka menggunakan IList<T>antarmuka.

System.Arrayhanya mengimplementasikan IList, jadi tidak akan mendapat manfaat dari optimasi ekstensi ini.
Oleh karena itu, saya sampaikan bahwa praktik terbaik adalah menggunakan .ToList()metode.
Jika Anda menggunakan salah satu metode ekstensi, atau meneruskan daftar ke metode lain, ada kemungkinan metode itu dioptimalkan untuk IList<T>.

Scott Rippey
sumber
16
Saya melakukan tes dan menemukan sesuatu yang mengejutkan. Array TIDAK mengimplementasikan IList <T>! Menggunakan Reflektor untuk menganalisis System. Array hanya mengungkapkan rantai pewarisan dari IList, ICollection, IEnumerable tetapi menggunakan run-time reflection Saya menemukan bahwa string [] memiliki rantai pewarisan IList, ICollection, IEnumerable, IList <string>, ICollection <string >, IEnumerable <string>. Karena itu, saya tidak punya jawaban yang lebih baik daripada @mquander!
Scott Rippey
@ScottRippey Ya. Pengamatan aneh yang Anda perhatikan sebenarnya adalah bagian dari "peretasan" - dan memiliki beberapa implikasi agak aneh mengenai "ukuran tetap" dan properti serupa (dengan beberapa inkonsistensi tergantung pada cara Anda melemparkannya). Ada beberapa komentar yang cukup besar menyentuh subjek ini di dalam kode sumber .net. Maaf karena tidak menautkan tetapi jika saya ingat dengan benar itu cukup mudah ditemukan (di dalam kelas array). (Dan ada juga pertanyaan SO besar yang membahas ketidakkonsistenan .... suatu tempat ...> __>)
AnorZaken
@ScottRippey hanya FYI Saya menemukan jawaban ini yang ada hubungannya dengan komentar Anda: stackoverflow.com/a/4482567/2063755
David Klempfner
14

Saya menemukan tolok ukur lain yang dilakukan orang di sini kurang, jadi inilah celah saya untuk itu. Beri tahu saya jika Anda menemukan sesuatu yang salah dengan metodologi saya.

/* 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

Anda dapat mengunduh Script LINQPad di sini .

Hasil: Kinerja ToArray vs ToList

Tweak kode di atas, Anda akan menemukan bahwa:

  1. Perbedaannya kurang signifikan ketika berhadapan dengan array yang lebih kecil . Lebih banyak iterasi, tetapi array yang lebih kecil
  2. Perbedaannya kurang signifikan ketika berhadapan dengan ints daripada strings.
  3. Menggunakan structs besar alih-alih strings membutuhkan lebih banyak waktu secara umum, tetapi tidak terlalu mengubah rasio.

Ini setuju dengan kesimpulan dari jawaban terpilih:

  1. Anda tidak mungkin melihat perbedaan kinerja kecuali kode Anda sering menghasilkan banyak daftar data besar. (Hanya ada perbedaan 200 ms saat membuat 1000 daftar string 100K masing-masing.)
  2. ToList() secara konsisten berjalan lebih cepat, dan akan menjadi pilihan yang lebih baik jika Anda tidak berencana untuk bertahan pada hasil untuk waktu yang lama.

Memperbarui

@ JonHanna menunjukkan bahwa tergantung pada implementasi Selectitu mungkin untuk ToList()atau ToArray()implementasi untuk memprediksi ukuran koleksi yang dihasilkan sebelumnya. Mengganti .Select(i => i)kode di atas dengan Where(i => true) hasil yang sangat mirip saat ini, dan lebih mungkin untuk melakukannya terlepas dari implementasi .NET.

Benchmark menggunakan Where, bukannya Select

StriplingWarrior
sumber
Dalam. NET Core kedua kasus harus lebih baik di sini daripada di netfx, karena akan menyadari ukuran akan menjadi 100000dan menggunakannya untuk mengoptimalkan keduanya ToList()dan ToArray(), dengan ToArray()menjadi sedikit lebih ringan karena tidak memerlukan operasi menyusut yang diperlukan jika tidak, yang merupakan satu tempat ToList()memiliki keuntungan. Contoh dalam pertanyaan masih akan kalah, karena Wherecara prediksi ukuran seperti itu tidak dapat dilakukan.
Jon Hanna
@ JonHanna: Terima kasih atas umpan balik cepatnya. Saya tidak tahu. NET Core membuat optimasi itu. Itu keren. Dalam kode saya, .Select(i => i)bisa diganti dengan .Where(i => true)untuk memperbaikinya.
StriplingWarrior
Ya, itu akan menghentikan optimasi yang berdampak pada corefx. Mungkin menarik untuk memiliki ukuran yang kekuatan dua (yang seharusnya memberikan ToArray()keuntungan) dan yang tidak, seperti di atas, dan membandingkan hasilnya.
Jon Hanna
@ JonHanna: Menariknya, ToArray() masih kalah dalam skenario kasus terbaik. Dengan Math.Pow(2, 15)elemen, itu (ToList: 700ms, ToArray: 900ms). Menambahkan satu elemen lagi menabraknya (ToList: 925, ToArray: 1350). Saya ingin tahu apakah ToArraymasih menyalin array bahkan ketika itu sudah ukuran yang sempurna? Mereka mungkin mengira itu adalah kejadian yang cukup langka sehingga tidak sepadan dengan persyaratan tambahan.
StriplingWarrior
Itu tidak menyalin pada pencocokan ukuran yang tepat, bahkan sebelum kami mulai mengoptimalkannya di corefx, jadi itu adalah kasus di mana ia mendapat istirahat paling banyak.
Jon Hanna
12

Anda harus mendasarkan keputusan Anda untuk memilih ToListatau ToArrayberdasarkan pada apa yang idealnya pilihan desain. Jika Anda ingin koleksi yang hanya dapat diulang dan diakses dengan indeks, pilih ToArray. Jika Anda ingin kemampuan tambahan untuk menambah dan menghapus dari koleksi nanti tanpa banyak kesulitan, maka lakukan ToList(tidak terlalu Anda tidak dapat menambahkan ke array, tapi itu biasanya bukan alat yang tepat untuk itu).

Jika masalah kinerja, Anda juga harus mempertimbangkan apa yang lebih cepat untuk beroperasi. Secara realistis, Anda tidak akan menelepon ToListatau ToArrayjutaan kali, tetapi mungkin bekerja pada koleksi yang diperoleh sejuta kali. Dalam hal itu []lebih baik, karena List<>ini []dengan beberapa overhead. Lihat utas ini untuk beberapa perbandingan efisiensi: Mana yang lebih efisien: Daftar <int> atau int []

Dalam tes saya sendiri beberapa waktu lalu, saya menemukan ToArraylebih cepat. Dan saya tidak yakin seberapa miringnya tes itu. Perbedaan kinerja sangat tidak signifikan, yang hanya dapat terlihat jika Anda menjalankan kueri ini dalam satu lingkaran jutaan kali.

nawfal
sumber
2
Ya - jika kompiler tahu bahwa Anda melakukan iterasi melalui array (bukan IEnumerable <>), ia dapat mengoptimalkan iterasi secara signifikan.
RobSiklos
12

Jawaban yang sangat terlambat tetapi saya pikir ini akan sangat membantu bagi para googler.

Mereka berdua payah ketika mereka dibuat menggunakan LINQ. Keduanya menerapkan kode yang sama untuk mengubah ukuran buffer jika perlu . ToArrayinternal menggunakan kelas untuk mengkonversi IEnumerable<>ke array, dengan mengalokasikan array 4 elemen. Jika itu tidak cukup daripada menggandakan ukuran dengan membuat array baru, gandakan ukuran arus dan menyalin array saat ini ke sana. Pada akhirnya itu mengalokasikan array baru jumlah item Anda. Jika kueri Anda mengembalikan 129 elemen maka ToArray akan membuat 6 alokasi dan operasi penyalinan memori untuk membuat array 256 elemen dan kemudian array 129 yang lain untuk dikembalikan. begitu banyak untuk efisiensi memori.

ToList melakukan hal yang sama, tetapi melompati alokasi terakhir karena Anda dapat menambahkan item di masa depan. Daftar tidak peduli apakah itu dibuat dari permintaan LINQ atau dibuat secara manual.

untuk pembuatan Daftar lebih baik dengan memori tetapi lebih buruk dengan cpu karena daftar adalah solusi umum setiap tindakan memerlukan pemeriksaan rentang tambahan untuk pemeriksaan jangkauan internal .net untuk array.

Jadi, jika Anda akan mengulangi melalui set hasil Anda terlalu banyak, maka array baik karena itu berarti lebih sedikit rentang pemeriksaan daripada daftar, dan kompiler umumnya mengoptimalkan array untuk akses berurutan.

Alokasi inisialisasi daftar bisa lebih baik jika Anda menentukan parameter kapasitas saat Anda membuatnya. Dalam hal ini hanya akan mengalokasikan array sekali, dengan asumsi Anda tahu ukuran hasilnya. ToListdari linq tidak menentukan kelebihan beban untuk menyediakannya, jadi kami harus membuat metode ekstensi kami yang membuat daftar dengan kapasitas yang diberikan dan kemudian menggunakannya List<>.AddRange.

Untuk menyelesaikan jawaban ini saya harus menulis kalimat berikut

  1. Pada akhirnya, Anda dapat menggunakan ToArray, atau ToList, kinerja tidak akan terlalu berbeda (lihat jawaban @EMP).
  2. Anda menggunakan C #. Jika Anda membutuhkan kinerja maka jangan khawatir tentang menulis kode kinerja tinggi, tetapi khawatir tidak menulis kode kinerja buruk.
  3. Selalu targetkan x64 untuk kode kinerja tinggi. AFAIK, x64 JIT didasarkan pada kompiler C ++, dan melakukan beberapa hal lucu seperti optimisasi rekursi ekor.
  4. Dengan 4,5, Anda juga dapat menikmati optimasi berpandu profil dan JIT multi-inti.
  5. Akhirnya, Anda dapat menggunakan pola async / wait untuk memprosesnya lebih cepat.
Erdogan Kurtur
sumber
Mereka berdua payah? Apakah Anda memiliki ide alternatif yang tidak memerlukan alokasi memori yang berlebihan?
nawfal
Dalam konteks pertanyaan, ya, mereka berdua payah tetapi karena alokasi yang berlebihan, dan tidak ada yang lain. Untuk mengurangi alokasi yang berlebihan, seseorang dapat menggunakan daftar tertaut dengan mengorbankan memori dan kecepatan iterasi. Pada akhirnya, inilah yang kami lakukan, kami melakukan trade off. Gagasan lain jika membuat daftar dengan kapasitas 200 (misalnya) dan kemudian memuat item. Ini juga akan mengurangi redundansi, tetapi array selalu lebih cepat, jadi ini merupakan trade off yang lain.
Erdogan Kurtur
Buat daftar 200 ? Itu mungkin menghindari mengubah ukuran, tetapi saya berbicara tentang memori yang digunakan berlebihan. Anda tidak dapat menahannya karena tidak ada pengetahuan awal tentang ukurannya. Anda sudah dapat menentukan kapasitas dalam konstruktor dari List<T>, tetapi ketika Anda tidak atau ketika Anda tidak bisa, Anda tidak bisa menahannya.
nawfal
2
satu-satunya data yang berlebihan dalam memori adalah isi array yang merupakan daftar pointer (dalam hal ini). satu juta 64 bit pointer membutuhkan memori hingga 8 MB, yang tidak seberapa dibandingkan dengan satu juta objek yang mereka tunjuk. 200 hanyalah angka, dan memiliki peluang untuk mengurangi jumlah panggilan ubah ukuran maksimal 5 kali. dan ya, kami tidak bisa menahannya. kami tidak memiliki opsi yang lebih baik. Saya tidak punya solusi yang lebih baik, tetapi ini tidak berarti saya tidak diizinkan untuk mengatakan di mana masalahnya.
Erdogan Kurtur
1
hmm pada akhirnya itu adalah tempat Anda menggambar garis. Saya suka implementasi saat ini. Nada jawaban Anda membuat saya berpikir itu adalah kritik daripada di mana masalahnya :)
nawfal
7

Ini adalah pertanyaan lama - tetapi untuk kepentingan pengguna yang menemukan itu, ada juga dan alternatif 'Memoizing' the Enumerable - yang memiliki efek caching dan menghentikan beberapa enumerasi pernyataan Linq, yang merupakan ToArray () dan ToList () digunakan untuk banyak hal, meskipun atribut koleksi dari daftar atau array tidak pernah digunakan.

Memoize tersedia di RX / System. Lib interaktif, dan dijelaskan di sini: Lebih banyak LINQ dengan System.Interactive

(Dari blog Bart De'Smet yang merupakan bacaan yang sangat dianjurkan jika Anda banyak bekerja dengan Linq ke Objects)

Frep D-Oronge
sumber
4

Salah satu opsi adalah menambahkan metode ekstensi Anda sendiri yang mengembalikan hanya baca ICollection<T> . Ini bisa lebih baik daripada menggunakan ToListatau ToArrayketika Anda tidak ingin menggunakan properti pengindeksan array / daftar, atau menambah / menghapus dari daftar.

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

Tes unit:

[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);
    }
}
Weston
sumber
Perlu dicatat bahwa kontrak pengumpulan read-only hanya menetapkan bahwa pengguna objek tidak boleh memodifikasinya, tetapi pemiliknya masih dapat melakukannya jika tetap menggunakan referensi yang menawarkan antarmuka yang bisa diubah. Untuk antarmuka yang menjamin bahwa struktur yang mendasarinya tidak akan pernah berubah, lihat koleksi abadi. Mengenai mengapa koleksi baca-tulis yang tidak berubah, atau hanya-baca, atau polos lebih baik atau lebih buruk, orang perlu titik referensi untuk perbandingan; tidak ada jawaban pamungkas (kalau tidak kita tidak harus memilih).
tgl
@tne Catatan Saya melakukan Tolist sebelum AsReadOnly, jadi tidak ada referensi ke yang bisa berubah yang mendasari.
barat
Anda sepenuhnya benar, dan itu mungkin cara terbaik untuk melakukan hal-hal sebelum koleksi abadi datang ke BCL (saya melihat beta pertama keluar sebulan setelah jawaban Anda).
mentega
Koleksi abadi ada untuk keamanan utas, di mana utas dapat berasumsi bahwa itu tidak akan berubah, dan jika ya, versi baru dibuat, alih-alih berpacu melawan pembaca dan mengubahnya saat mereka menggunakannya. Dengan cara ini, tidak ada yang perlu mendapatkan kunci.
doug65536
4

ToListAsync<T>() lebih disukai.

Dalam Entity Framework 6 kedua metode akhirnya memanggil ke metode internal yang sama, tetapi ToArrayAsync<T>()panggilan list.ToArray()di akhir, yang diimplementasikan sebagai

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

Begitu ToArrayAsync<T>()juga beberapa overhead, dengan demikian ToListAsync<T>()lebih disukai.

Stephen Zeng
sumber
1
Itu sebenarnya jawaban yang saya cari, bagaimana EF melakukannya. Saya ingin tahu bagaimana dengan EF Core.
Shimmy Weitzhandler
3

Pertanyaan lama tetapi pertanyaan baru sepanjang waktu.

Menurut sumber System.Linq.Enumerable , ToListcukup kembalikan a new List(source), sementara ToArraygunakan a new Buffer<T>(source).ToArray()untuk mengembalikan a T[].

Tentang alokasi memori:

Saat berjalan pada satu- IEnumerable<T>satunya objek, ToArrayjangan mengalokasikan memori sekali lagi ToList. Tetapi Anda tidak perlu mempedulikannya dalam banyak kasus, karena GC akan melakukan pengumpulan sampah saat dibutuhkan.

Tentang efisiensi runtime:

Mereka yang mempertanyakan pertanyaan ini dapat menjalankan kode berikut di komputer Anda sendiri, dan Anda akan mendapatkan jawaban Anda.

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

Saya mendapat hasil ini di mesin saya:

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]

Group2:

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

Group3:

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]

Group5:

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]

Karena batas stackoverflow untuk jumlah karakter dari jawaban, daftar sampel Group2 dan Group3 dihilangkan.

Seperti yang Anda lihat, itu benar-benar tidak penting untuk digunakan ToListatau ToArrydalam kebanyakan kasus.

Saat memproses IEnumerable<T>objek yang dihitung runtime , jika beban yang dibawa oleh perhitungan lebih berat dari alokasi memori dan operasi salin dari ToListdan ToArray, disparitas tidak signifikan ( C.ToList vs C.ToArraydan S.ToList vs S.ToArray).

Perbedaannya hanya dapat diamati pada IEnumerable<T>objek yang dihitung non-runtime ( C1.ToList vs C1.ToArraydan S1.ToList vs S1.ToArray). Tetapi perbedaan absolut (<60ms) masih dapat diterima pada satu juta objek kecil IEnumerable<T>. Bahkan, perbedaannya ditentukan oleh implementasi Enumerator<T>dari IEnumerable<T>. Jadi, jika program Anda benar-benar sangat sensitif tentang ini, Anda harus profil, profil, profil ! Akhirnya Anda mungkin akan menemukan bahwa bottleneck bukan pada ToListatau ToArray, tetapi detail enumerator.

Dan, hasil dari C2.ToList vs C2.ToArraydan S2.ToList vs S2.ToArraymenunjukkan bahwa, Anda benar-benar tidak perlu peduli ToListatau ToArraypada ICollection<T>objek yang dihitung non-runtime .

Tentu saja, ini hanya hasil pada mesin saya, waktu yang sebenarnya dihabiskan untuk operasi ini pada mesin yang berbeda tidak akan sama, Anda dapat mengetahui pada mesin Anda menggunakan kode di atas.

Satu-satunya alasan Anda perlu membuat pilihan adalah bahwa, Anda memiliki kebutuhan khusus List<T>atau T[], seperti yang dijelaskan oleh jawaban @Jeppe Stig Nielsen .

qaqz111
sumber
1

Bagi siapa pun yang tertarik menggunakan hasil ini di Linq-to-sql lain seperti

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

maka SQL yang dihasilkan sama apakah Anda menggunakan Daftar atau Array untuk myListOrArray. Sekarang saya tahu beberapa orang mungkin bertanya mengapa bahkan menyebutkan sebelum pernyataan ini, tetapi ada perbedaan antara SQL yang dihasilkan dari IQueryable vs (Daftar atau Array).

Gary
sumber