Saat menggunakan ToList()
, apakah ada dampak kinerja yang perlu dipertimbangkan?
Saya sedang menulis kueri untuk mengambil file dari direktori, yang merupakan kueri:
string[] imageArray = Directory.GetFiles(directory);
Namun, karena saya lebih suka bekerja dengan List<>
, saya memutuskan untuk memasukkan ...
List<string> imageList = Directory.GetFiles(directory).ToList();
Jadi, apakah ada semacam dampak kinerja yang harus dipertimbangkan ketika memutuskan untuk melakukan konversi seperti ini - atau hanya dipertimbangkan ketika berhadapan dengan sejumlah besar file? Apakah ini konversi yang dapat diabaikan?
c#
arrays
performance
list
Cody
sumber
sumber
List<T>
mendukungT[]
jika itu membuat kode lebih logis / dapat dibaca / dipelihara (kecuali tentu saja konversi itu menyebabkan masalah kinerja yang nyata dalam hal ini saya akan kembali kunjungi saya kira).Add
atauRemove
, saya akan membiarkannyaIEnumerable<T>
(atau bahkan lebih baikvar
)EnumerateFiles
daripadaGetFiles
, jadi hanya satu array yang akan dibuat.GetFiles(directory)
, seperti yang diterapkan di .NET saat ini, cukup banyaknew List<string>(EnumerateFiles(directory)).ToArray()
. JadiGetFiles(directory).ToList()
buat daftar, buat array dari itu, lalu buat daftar lagi. Seperti yang dikatakan 2kay, Anda harus memilih untuk melakukannya diEnumerateFiles(directory).ToList()
sini.Jawaban:
IEnumerable.ToList()
Ya,
IEnumerable<T>.ToList()
memang memiliki dampak kinerja, ini adalah operasi O (n) meskipun kemungkinan hanya membutuhkan perhatian dalam operasi kritis kinerja.The
ToList()
operasi akan menggunakanList(IEnumerable<T> collection)
konstruktor. Konstruktor ini harus membuat salinan array (lebih umumIEnumerable<T>
), jika tidak modifikasi di masa depan dari array asli akan berubah pada sumberT[]
juga yang tidak diinginkan secara umum.Saya ingin mengulangi ini hanya akan membuat perbedaan dengan daftar besar, menyalin potongan memori adalah operasi yang cukup cepat untuk dilakukan.
Tip praktis,
As
vsTo
Anda akan melihat di LINQ ada beberapa metode yang dimulai dengan
As
(sepertiAsEnumerable()
) danTo
(sepertiToList()
). Metode yang dimulai denganTo
memerlukan konversi seperti di atas (mis. Dapat mempengaruhi kinerja), dan metode yang memulai denganAs
tidak dan hanya akan memerlukan beberapa pemain atau operasi sederhana.Rincian tambahan tentang
List<T>
Berikut ini sedikit lebih detail tentang bagaimana cara
List<T>
kerjanya jika Anda tertarik :)A
List<T>
juga menggunakan konstruk yang disebut array dinamis yang perlu diubah ukurannya sesuai permintaan, peristiwa ini mengubah ukuran isi dari array lama ke array baru. Jadi itu dimulai dari kecil dan bertambah besar jika diperlukan .Ini adalah perbedaan antara atribut
Capacity
dan . mengacu pada ukuran array di belakang layar, adalah jumlah item yang selalu ada . Jadi, ketika suatu item ditambahkan ke daftar, meningkatkannya melewati , ukuran dua kali lipat dan array disalin.Count
List<T>
Capacity
Count
List<T>
<= Capacity
Capacity
List<T>
sumber
List(IEnumerable<T> collection)
konstruktor memeriksa apakah parameter pengumpulanICollection<T>
dan kemudian membuat array internal baru dengan ukuran yang diperlukan segera. Jika pengumpulan parameter tidakICollection<T>
, konstruktor mengulanginya dan memanggilAdd
setiap elemen.Ya tentu saja. Secara teoritis bahkan
i++
memiliki dampak kinerja, memperlambat program untuk beberapa detik.Apa yang
.ToList
harus dilakukanKetika Anda memohon
.ToList
, kode panggilanEnumerable.ToList()
yang merupakan metode ekstensi itureturn new List<TSource>(source)
. Dalam konstruktor yang sesuai, di bawah keadaan terburuk, ia melewati wadah item dan menambahkannya satu per satu ke dalam wadah baru. Jadi perilakunya sedikit mempengaruhi kinerja. Tidak mungkin menjadi leher botol kinerja aplikasi Anda.Apa yang salah dengan kode dalam pertanyaan
Directory.GetFiles
melewati folder dan mengembalikan semua nama file dengan segera ke dalam memori, ia memiliki risiko potensial bahwa string [] menghabiskan banyak memori, memperlambat segalanya.Apa yang harus dilakukan?
Tergantung. Jika Anda (dan juga logika bisnis Anda) menjamin bahwa jumlah file di folder selalu kecil, kode dapat diterima. Tetapi masih disarankan untuk menggunakan versi malas:
Directory.EnumerateFiles
di C # 4. Ini lebih seperti kueri, yang tidak akan segera dieksekusi, Anda dapat menambahkan lebih banyak kueri seperti:yang akan berhenti mencari jalan segera setelah file yang namanya mengandung "myfile" ditemukan. Ini jelas memiliki kinerja yang lebih baik
.GetFiles
.sumber
Ya ada. Menggunakan metode ekstensi
Enumerable.ToList()
akan membangunList<T>
objek baru dariIEnumerable<T>
kumpulan sumber yang tentu saja memiliki dampak kinerja.Namun, pemahaman
List<T>
dapat membantu Anda menentukan apakah dampak kinerja itu signifikan.List<T>
menggunakan array (T[]
) untuk menyimpan elemen daftar. Array tidak dapat diperpanjang setelah dialokasikan sehinggaList<T>
akan menggunakan array berukuran lebih besar untuk menyimpan elemen daftar. KetikaList<T>
tumbuh melebihi ukuran array yang mendasarinya array baru harus dialokasikan dan isi array lama harus disalin ke array yang lebih besar baru sebelum daftar dapat tumbuh.Ketika yang baru
List<T>
dibangun dariIEnumerable<T>
ada dua kasus:Implementasi pengumpulan sumber
ICollection<T>
: KemudianICollection<T>.Count
digunakan untuk mendapatkan ukuran yang tepat dari koleksi sumber dan array backing yang cocok dialokasikan sebelum semua elemen dari koleksi sumber disalin ke array backing menggunakanICollection<T>.CopyTo()
. Operasi ini cukup efisien dan mungkin akan dipetakan ke beberapa instruksi CPU untuk menyalin blok memori. Namun, dalam hal kinerja, memori diperlukan untuk larik baru dan siklus CPU diperlukan untuk menyalin semua elemen.Jika tidak, ukuran kumpulan sumber tidak diketahui dan enumerator
IEnumerable<T>
digunakan untuk menambahkan setiap elemen sumber satu per satu ke yang baruList<T>
. Awalnya array dukungan kosong dan array ukuran 4 dibuat. Kemudian ketika array ini terlalu kecil ukurannya menjadi dua kali lipat sehingga array backing tumbuh seperti ini 4, 8, 16, 32 dll. Setiap kali array backing tumbuh itu harus realokasi dan semua elemen yang disimpan sejauh ini harus disalin. Operasi ini jauh lebih mahal dibandingkan dengan kasus pertama di mana array dengan ukuran yang benar dapat dibuat segera.Juga, jika koleksi sumber Anda mengandung katakanlah 33 elemen daftar akan berakhir dengan menggunakan array 64 elemen yang membuang-buang memori.
Dalam kasus Anda, pengumpulan sumber adalah array yang mengimplementasikan
ICollection<T>
sehingga dampak kinerja bukanlah sesuatu yang harus Anda perhatikan kecuali jika array sumber Anda sangat besar. MemanggilToList()
hanya akan menyalin array sumber dan membungkusnya dalam suatuList<T>
objek. Bahkan kinerja case kedua bukanlah sesuatu yang perlu dikhawatirkan untuk koleksi kecil.sumber
Masalah dengan skenario Anda yang tepat adalah bahwa yang pertama dan terpenting perhatian nyata Anda tentang kinerja akan berasal dari kecepatan hard drive dan efisiensi cache drive.
Dari perspektif itu, dampaknya jelas dapat diabaikan sampai-sampai TIDAK itu tidak perlu dipertimbangkan.
TETAPI SAJA jika Anda benar-benar membutuhkan fitur
List<>
struktur untuk membuat Anda lebih produktif, atau algoritme Anda lebih ramah, atau beberapa keuntungan lainnya. Jika tidak, Anda hanya dengan sengaja menambahkan hit kinerja yang tidak signifikan, tanpa alasan sama sekali. Dalam hal ini, tentu saja, Anda tidak boleh melakukannya! :)sumber
ToList()
membuat Daftar baru dan memasukkan unsur-unsur di dalamnya yang berarti ada biaya terkait dengan melakukanToList()
. Dalam kasus pengumpulan kecil itu tidak akan menjadi biaya yang sangat mencolok tetapi memiliki koleksi besar dapat menyebabkan kinerja yang hit jika menggunakan ToList.Secara umum Anda tidak boleh menggunakan ToList () kecuali jika pekerjaan yang Anda lakukan tidak dapat dilakukan tanpa mengubah koleksi menjadi Daftar. Misalnya jika Anda hanya ingin mengulang melalui koleksi, Anda tidak perlu melakukan ToList
Jika Anda melakukan kueri terhadap sumber data misalnya Basis data menggunakan LINQ ke SQL maka biaya melakukan ToList jauh lebih karena ketika Anda menggunakan ToList dengan LINQ ke SQL alih-alih melakukan Eksekusi Tertunda yaitu memuat item saat diperlukan (yang dapat bermanfaat dalam banyak skenario) secara instan memuat item dari Database ke memori
sumber
Ini akan seefisien melakukan:
Jika Anda membongkar kode sumber konstruktor yang mengambil
IEnumerable<T>
, Anda akan melihatnya akan melakukan beberapa hal:Panggil
collection.Count
, jadi jikacollection
adaIEnumerable<T>
, itu akan memaksa eksekusi. Jikacollection
array, daftar, dll itu harusO(1)
.Jika
collection
mengimplementasikanICollection<T>
, itu akan menyimpan item dalam array internal menggunakanICollection<T>.CopyTo
metode ini. Ini harus menjadiO(n)
, menjadin
panjang koleksi.Jika
collection
tidak menerapkanICollection<T>
, itu akan beralih melalui item koleksi, dan akan menambahkannya ke daftar internal.Jadi, ya, itu akan menghabiskan lebih banyak memori, karena harus membuat daftar baru, dan dalam kasus terburuk, itu akan
O(n)
, karena akan beralih melaluicollection
untuk membuat salinan setiap elemen.sumber
0(n)
manan
jumlah total byte yang disimpan string dalam koleksi asli, bukan jumlah elemen (lebih tepatnya n = bytes / ukuran kata)bool
,int
, Dll)? Anda tidak benar-benar harus membuat salinan setiap string dalam koleksi. Anda cukup menambahkannya ke daftar baru.Mempertimbangkan kinerja mengambil daftar file,
ToList()
dapat diabaikan. Tetapi tidak benar-benar untuk skenario lain. Itu benar-benar tergantung di mana Anda menggunakannya.Saat memanggil array, daftar, atau koleksi lain, Anda membuat salinan koleksi sebagai
List<T>
. Kinerja di sini tergantung pada ukuran daftar. Anda harus melakukannya ketika benar-benar diperlukan.Dalam contoh Anda, Anda menyebutnya pada array. Itu beralih di atas array dan menambahkan item satu per satu ke daftar yang baru dibuat. Jadi dampak kinerja tergantung pada jumlah file.
Saat memanggil pada
IEnumerable<T>
, Anda terwujud dalamIEnumerable<T>
(biasanya query).sumber
ToList Akan membuat daftar baru dan menyalin elemen dari sumber asli ke daftar yang baru dibuat sehingga hanya menyalin elemen dari sumber asli dan tergantung pada ukuran sumber
sumber