Potongan kode mana yang akan memberikan kinerja yang lebih baik? Segmen kode di bawah ini ditulis dalam C #.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2.
foreach(MyType current in list)
{
current.DoSomething();
}
c#
performance
for-loop
foreach
Kthevar
sumber
sumber
list
benar - benar memiliki anggotacount
, bukanCount
.Jawaban:
Yah, sebagian tergantung pada jenis pastinya
list
. Ini juga akan tergantung pada CLR persis yang Anda gunakan.Apakah itu signifikan atau tidak, akan tergantung pada apakah Anda melakukan pekerjaan nyata dalam loop. Di hampir semua kasus, perbedaan kinerja tidak akan signifikan, tetapi perbedaan pada keterbacaan mendukung
foreach
loop.Saya pribadi menggunakan LINQ untuk menghindari "jika" juga:
EDIT: Bagi Anda yang mengklaim bahwa iterasi
List<T>
dengan aforeach
menghasilkan kode yang sama denganfor
loop, inilah bukti bahwa itu tidak:Menghasilkan IL dari:
Kompiler memperlakukan array secara berbeda,
foreach
pada dasarnya mengubah sebuah loop menjadi sebuahfor
loop, tetapi tidakList<T>
. Berikut kode yang setara untuk sebuah array:Menariknya, saya tidak dapat menemukan ini didokumentasikan dalam spesifikasi C # 3 di mana pun ...
sumber
List<T>
.foreach
over array setara denganfor
anyway. Selalu buat kode untuk keterbacaan terlebih dahulu, lalu hanya optimalkan mikro jika Anda memiliki bukti bahwa hal itu memberikan manfaat kinerja yang terukur.Sebuah
for
loop dikompilasi ke kode yang kira-kira setara dengan ini:Dimana sebuah
foreach
loop dikompilasi ke kode yang kira-kira setara dengan ini:Jadi seperti yang Anda lihat, itu semua akan tergantung pada bagaimana enumerator diimplementasikan versus bagaimana pengindeks daftar diimplementasikan. Ternyata pencacah untuk tipe berdasarkan array biasanya ditulis seperti ini:
Jadi seperti yang Anda lihat, dalam hal ini tidak akan membuat banyak perbedaan, namun enumerator untuk daftar tertaut mungkin akan terlihat seperti ini:
Dalam .NET Anda akan menemukan bahwa kelas LinkedList <T> bahkan tidak memiliki pengindeks, jadi Anda tidak akan bisa melakukan perulangan for pada daftar tertaut; tetapi jika Anda bisa, pengindeks harus ditulis seperti ini:
Seperti yang Anda lihat, memanggil ini beberapa kali dalam satu putaran akan menjadi jauh lebih lambat daripada menggunakan enumerator yang dapat mengingat di mana posisinya dalam daftar.
sumber
Tes mudah untuk semi-validasi. Saya melakukan tes kecil, hanya untuk melihat. Ini kodenya:
Dan inilah bagian foreach:
Ketika saya mengganti for dengan foreach - foreach 20 milidetik lebih cepat - secara konsisten . Untuk adalah 135-139ms sedangkan foreach adalah 113-119ms. Saya bertukar bolak-balik beberapa kali, memastikan itu bukan proses yang baru saja dimulai.
Namun, ketika saya menghapus foo dan pernyataan if, untuk lebih cepat 30 md (untuk masing-masing 88 md dan untuk 59 md). Keduanya adalah cangkang kosong. Saya berasumsi bahwa foreach benar-benar melewati variabel dimana for hanya menaikkan variabel. Jika saya menambahkan
Kemudian untuk menjadi lambat sekitar 30ms. Saya berasumsi ini ada hubungannya dengan itu membuat foo dan mengambil variabel dalam array dan menugaskannya ke foo. Jika Anda hanya mengakses intList [i] maka Anda tidak memiliki penalti itu.
Sejujurnya .. Saya berharap foreach menjadi sedikit lebih lambat dalam semua keadaan, tetapi tidak cukup penting di sebagian besar aplikasi.
edit: berikut adalah kode baru menggunakan saran Jons (134217728 adalah int terbesar yang dapat Anda miliki sebelum pengecualian System.OutOfMemory dilempar):
Dan inilah hasilnya:
Menghasilkan data. Menghitung loop for: 2458ms Menghitung foreach loop: 2005ms
Menukar mereka untuk melihat apakah itu berkaitan dengan urutan hal-hal menghasilkan hasil yang sama (hampir).
sumber
Catatan: jawaban ini lebih berlaku untuk Java daripada C #, karena C # tidak memiliki pengindeks
LinkedLists
, tapi saya pikir poin umum masih berlaku.Jika yang
list
Anda kerjakan kebetulan adalah aLinkedList
, kinerja kode pengindeks ( pengaksesan gaya larik ) jauh lebih buruk daripada menggunakanIEnumerator
dariforeach
, untuk daftar besar.Saat Anda mengakses elemen 10.000 dalam
LinkedList
menggunakan sintaks pengindeks :,list[10000]
daftar tertaut akan dimulai pada simpul kepala, dan melintasiNext
-pointer sepuluh ribu kali, hingga mencapai objek yang benar. Jelas, jika Anda melakukan ini dalam satu lingkaran, Anda akan mendapatkan:Saat Anda memanggil
GetEnumerator
(secara implisit menggunakanforach
-syntax), Anda akan mendapatkanIEnumerator
objek yang memiliki penunjuk ke node kepala. Setiap kali Anda memanggilMoveNext
, penunjuk tersebut dipindahkan ke node berikutnya, seperti:Seperti yang Anda lihat, dalam kasus
LinkedList
s, metode pengindeks array menjadi lebih lambat dan lebih lambat, semakin lama Anda melakukan perulangan (harus melalui penunjuk kepala yang sama berulang kali). Padahal yangIEnumerable
adil beroperasi dalam waktu yang konstan.Tentu saja, seperti yang dikatakan Jon ini sangat tergantung pada jenisnya
list
, jikalist
bukan aLinkedList
, tapi sebuah array, perilakunya sangat berbeda.sumber
LinkedList<T>
dokumen di MSDN, dan ini memiliki API yang lumayan bagus. Yang terpenting, ia tidak memilikiget(int index)
metode, seperti Java. Namun, saya kira intinya masih berlaku untuk struktur data seperti daftar lainnya yang mengekspos pengindeks yang lebih lambat dari yang spesifikIEnumerator
.Seperti yang orang lain sebutkan meskipun kinerja sebenarnya tidak terlalu penting, foreach akan selalu sedikit lebih lambat karena
IEnumerable
/IEnumerator
penggunaan dalam loop. Kompilator menerjemahkan konstruksi menjadi panggilan pada antarmuka itu dan untuk setiap langkah fungsi + properti dipanggil dalam konstruksi foreach.Ini adalah ekspansi ekuivalen dari konstruksi di C #. Anda dapat membayangkan bagaimana dampak kinerja dapat bervariasi berdasarkan implementasi MoveNext dan Current. Sedangkan dalam akses array, Anda tidak memiliki dependensi itu.
sumber
List<T>
sini, maka masih ada hit (mungkin sebaris) untuk memanggil pengindeks. Ini tidak seperti itu akses array logam kosong.Setelah membaca cukup banyak argumen bahwa "loop foreach harus lebih disukai untuk keterbacaan", saya dapat mengatakan bahwa reaksi pertama saya adalah "apa"? Keterbacaan, secara umum, bersifat subjektif dan, dalam contoh khusus ini, bahkan lebih. Untuk seseorang dengan latar belakang pemrograman (praktis, setiap bahasa sebelum Java), foreach lebih mudah dibaca daripada foreach loop. Selain itu, orang yang sama mengklaim bahwa foreach loop lebih mudah dibaca, juga merupakan pendukung LINQ dan "fitur" lain yang membuat kode sulit dibaca dan dipelihara, sesuatu yang membuktikan poin di atas.
Tentang pengaruhnya terhadap kinerja, lihat jawaban atas pertanyaan ini .
EDIT: Ada koleksi di C # (seperti HashSet) yang tidak memiliki pengindeks. Dalam koleksi ini, foreach adalah satu-satunya cara untuk mengulang dan itu satu-satunya kasus yang menurut saya harus digunakan untuk itu .
sumber
Ada fakta menarik lainnya yang dapat dengan mudah terlewatkan saat menguji kecepatan kedua loop: Menggunakan mode debug tidak memungkinkan kompiler mengoptimalkan kode menggunakan pengaturan default.
Ini membawa saya ke hasil yang menarik bahwa foreach lebih cepat daripada dalam mode debug. Sedangkan for ist lebih cepat dari foreach dalam mode rilis. Jelas compiler memiliki cara yang lebih baik untuk mengoptimalkan perulangan for daripada foreach loop yang mengkompromikan beberapa pemanggilan metode. Perulangan for adalah cara yang sangat mendasar sehingga mungkin saja ini dioptimalkan oleh CPU itu sendiri.
sumber
Dalam contoh yang Anda berikan, pasti lebih baik menggunakan
foreach
loop daripadafor
loop.foreach
Konstruksi standar bisa lebih cepat (1,5 siklus per langkah) daripada yang sederhanafor-loop
(2 siklus per langkah), kecuali loop telah dibuka (1,0 siklus per langkah).Jadi untuk kode sehari-hari, kinerja bukanlah alasan untuk menggunakan yang lebih kompleks
for
,while
ataudo-while
konstruksi.Lihat tautan ini: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
sumber
Anda dapat membacanya di Deep .NET - Iterasi bagian 1
itu mencakup hasil (tanpa inisialisasi pertama) dari kode sumber .NET sampai ke pembongkaran.
misalnya - Array Iteration dengan foreach loop:
dan - daftar iterasi dengan foreach loop:
dan hasil akhirnya:
sumber