Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Foreach atau For – Itulah Pertanyaannya

Diskusi tentang perbedaan preferensi antara FOREACH dan FOR bukanlah hal baru. Kita semua tahu bahwa FOREACH lebih lambat, tetapi tidak semua tahu mengapa.

Ketika saya mulai belajar .NET, satu orang mengatakan kepada saya bahwa FOREACH dua kali lebih lambat dari FOR. Dia mengatakan ini tanpa alasan. Saya menerima begitu saja.

Akhirnya, saya memutuskan untuk mengeksplorasi perbedaan kinerja loop FOREACH dan FOR, dan menulis artikel ini untuk membahas nuansa.
Mari kita lihat kode berikut:

foreach (var item in Enumerable.Range(0, 128))
{
  Console.WriteLine(item);
}

FOREACH adalah gula sintaksis. Dalam kasus khusus ini, kompilator mengubahnya menjadi kode berikut:

IEnumerator<int> enumerator = Enumerable.Range(0, 128).GetEnumerator();
try
 {
   while (enumerator.MoveNext())
   {
     int item = enumerator.Current;
     Console.WriteLine(item);
   }
 }
finally
 {
  if (enumerator != null)
  {
   enumerator.Dispose();
  }
}

Mengetahui hal ini, kita dapat mengasumsikan alasan mengapa FOREACH lebih lambat dari FOR:

  • Sebuah objek baru sedang dibuat. Itu disebut Pencipta.
  • Metode MoveNext dipanggil pada setiap iterasi.
  • Setiap iterasi mengakses properti Current.

Itu dia! Namun, itu tidak semudah kedengarannya.

Untungnya (atau sayangnya), C#/CLR dapat melakukan optimasi pada saat run time. Pro adalah bahwa kode bekerja lebih cepat. Kontra – pengembang harus menyadari pengoptimalan ini.

Array adalah tipe yang sangat terintegrasi ke dalam CLR, dan CLR menyediakan sejumlah optimasi untuk tipe ini. Loop FOREACH adalah entitas yang dapat diubah, yang merupakan aspek kunci dari kinerja. Nanti di artikel ini, kita akan membahas cara melakukan iterasi melalui array dan daftar dengan bantuan metode statis Array.ForEach dan metode List.ForEach.

Metode Uji

static double ArrayForWithoutOptimization(int[] array)
{
   int sum = 0;
   var watch = Stopwatch.StartNew();
   for (int i = 0; i < array.Length; i++)
     sum += array[i];
    watch.Stop();
    return watch.Elapsed.TotalMilliseconds;
}

static double ArrayForWithOptimization(int[] array)
{
   int length = array.Length;
   int sum = 0;
   var watch = Stopwatch.StartNew();
    for (int i = 0; i < length; i++)
      sum += array[i];
    watch.Stop();
     return watch.Elapsed.TotalMilliseconds;
}

static double ArrayForeach(int[] array)
{
  int sum = 0;
  var watch = Stopwatch.StartNew();
   foreach (var item in array)
    sum += item;
  watch.Stop();
  return watch.Elapsed.TotalMilliseconds;
}

static double ArrayForEach(int[] array)
{
  int sum = 0;
  var watch = Stopwatch.StartNew();
  Array.ForEach(array, i => { sum += i; });
  watch.Stop();
  return watch.Elapsed.TotalMilliseconds;
}

Kondisi pengujian:

  • Opsi “Optimalkan kode” diaktifkan.
  • Jumlah elemen sama dengan 100 000 000 (baik dalam array maupun daftar).
  • Spesifikasi PC:Intel Core i-5 dan RAM 8 GB.

Array

Diagram menunjukkan bahwa FOR dan FOREACH menghabiskan jumlah waktu yang sama saat melakukan iterasi melalui array. Dan itu karena optimasi CLR mengubah FOREACH menjadi FOR, dan menggunakan panjang array sebagai batas iterasi maksimum. Tidak masalah apakah panjang array di-cache atau tidak (bila menggunakan FOR), hasilnya hampir sama.

Ini mungkin terdengar aneh, tetapi caching panjang array dapat mempengaruhi kinerja. Saat menggunakan array .Panjang sebagai batas iterasi, JIT menguji indeks untuk mencapai batas kanan di luar siklus. Pemeriksaan ini hanya dilakukan sekali.
Sangat mudah untuk menghancurkan optimasi ini. Kasus ketika variabel di-cache hampir tidak dioptimalkan.

Array.foreach menunjukkan hasil terburuk. Implementasinya cukup sederhana:

public static void ForEach<T>(T[] array, Action<T> action)
 {
  for (int index = 0; index < array.Length; ++index)
    action(array[index]);
 }

Lalu mengapa berjalan sangat lambat? Ini menggunakan FOR di bawah tenda. Nah, alasannya adalah memanggil delegasi ACTION. Faktanya, sebuah metode dipanggil pada setiap iterasi, yang menurunkan kinerja. Selain itu, delegasi dipanggil tidak secepat yang kita inginkan.

Daftar

Hasilnya benar-benar berbeda. Saat mengulangi daftar, FOR dan FOREACH menunjukkan hasil yang berbeda. Tidak ada optimasi. FOR (dengan caching panjang daftar) menunjukkan hasil terbaik, sedangkan FOREACH lebih dari 2 kali lebih lambat. Itu karena berhubungan dengan MoveNext dan Current di bawah tenda. List.ForEach serta Array.ForEach menunjukkan hasil terburuk. Delegasi selalu dipanggil secara virtual. Implementasi metode ini terlihat seperti ini:

public void ForEach(Action<T> action)
{
  int num = this._version;
   for (int index = 0; index < this._size && num == this._version; ++index)
     action(this._items[index]);
   if (num == this._version)
     return;
   ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}

Setiap iterasi memanggil delegasi Tindakan. Itu juga memeriksa apakah daftar diubah dan jika demikian, pengecualian dilemparkan.

List secara internal menggunakan model berbasis array dan metode ForEach menggunakan indeks array untuk beralih, yang secara signifikan lebih cepat daripada menggunakan pengindeks.

Nomor tertentu

  1. Perulangan FOR tanpa caching panjang dan FOREACH bekerja sedikit lebih cepat pada array dibandingkan FOR dengan caching panjang.
  2. Array.Foreach kinerja kira-kira 6 kali lebih lambat dari kinerja FOR / FOREACH.
  3. Perulangan FOR tanpa caching panjang bekerja 3 kali lebih lambat pada daftar, dibandingkan dengan array.
  4. Perulangan FOR dengan caching panjang bekerja 2 kali lebih lambat pada daftar, dibandingkan dengan array.
  5. Perulangan FOREACH bekerja 6 kali lebih lambat pada daftar, dibandingkan dengan larik.

Berikut adalah papan pemimpin untuk daftar:

Dan untuk array:

Kesimpulan

Saya sangat menikmati investigasi ini, terutama proses penulisannya, dan saya harap Anda juga menikmatinya. Ternyata, FOREACH lebih cepat pada array daripada FOR dengan pengejaran panjang. Pada struktur daftar, FOREACH lebih lambat dari FOR.

Kode terlihat lebih baik saat menggunakan FOREACH, dan prosesor modern memungkinkan menggunakannya. Namun, jika Anda perlu mengoptimalkan basis kode Anda, lebih baik gunakan FOR.

Bagaimana menurut Anda, loop mana yang berjalan lebih cepat, FOR atau FOREACH?


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menangani Nilai NULL Secara Efektif dengan Fungsi SQL COALESCE untuk Pemula

  2. Pertandingan Terdekat, Bagian 3

  3. Opsi Basis Data/Pelaporan Penggunaan Paket

  4. Kejutan dan Asumsi Kinerja :GROUP BY vs. DISTINCT

  5. Kursor Ref Kuat Dengan Tipe Data Rekaman Berbasis Tabel