Foreach döngüsü hayatımızı kolaylaştırsa da bazı istisnalar dışında for döngüsü daha iyi performans göstermektedir. Bu durumu aşağıdaki kodu inceleyerek açıklamaya çalışalım. Performans ölçümü için System.Diagnostics.Stopwatch sınıfı kullanılmıştır.
var sayılar = Enumerable.Range(1, 1000000).ToList();
// foreach döngüsü var swForeach = new Stopwatch(); swForeach.Start(); try { foreach (int i in sayılar) { int j = i; } } finally { swForeach.Stop(); } Console.WriteLine("Süre: {0}", swForeach.Elapsed); // Süre: 00:00:00.0287938
// for döngüsü var swFor = new Stopwatch(); swFor.Start(); try { for (int i = 0; i < sayılar.Count; i++) { int j = sayılar[i]; } } finally { swFor.Stop(); } Console.WriteLine("Süre: {0}", swFor.Elapsed); // Süre: 00:00:00.0090397
Testler sonucunda for döngüsünün 3 kat daha hızlı tamamlandığı görülmektedir. Peki buna sebep olan nedir? Neden for döngüsü daha hızlı çalışmaktadır? Buna sebep olan derleyicinin foreach döngüsü için arka planda enumerator object kullanmasıdır. Derleyici tarafından foreach döngüsü için oluşturulan IL çıktısı aşağıdadır; Çıktıda kolayca fark edebileceğiniz üzere her bir döngüde sayılar listesinin Enumerator sınıfındaki metotlar çağrılmaktadır. Bu durum foreach döngüsünün daha yavaş çalışmasına sebep olmaktadır.
Koleksiyonlar üzerinde inline döngülerden kaçının Teknik olarak aşağıdaki iki kodunda aynı sonucu vereceğini ve arada bir hız farkı olmayacağını düşünebilirsiniz. Eğer böyle düşünüyorsanız aradaki farkı görünce fikriniz değişecektir.
// Kullanın List sayılar = Enumerable.Range(1, 1000000).ToList(); foreach (int item in sayılar) { var j = item; } //Süre: 00:00:00.0028942
// Kaçının foreach (int item in Enumerable.Range(1, 1000000).ToList()) { var j = item; } //Süre: 00:00:00.0136188
Test sonuçlarında lokal değişken olarak tanımlanmış koleksiyon ile döngü içerisinde inline olarak kullanılan koleksiyon arasındaki hız farkı bariz bir şekilde görülmektedir. Bu durum bir önceki örnekte bahsedilen Enumerator çağırma ile bağlantılıdır ve kaçınılması gerekir.
Gerektiğinde paralel döngüleri tercih edin Eğer döngü içerisinde yoğun işlemci gücü gerektiren kodlar çalıştırıyorsanız paralel döngüler performans artışı sağlayabilir. Bu döngüler standart döngüler gibi sıralı değil paralel olarak çalışmaktadır. Bu sayede işlemci gücü maksimum oranda kullanılır.
var anahtarlarPF = new string[600]; Parallel.For(0, anahtarlarPF.Length, i => anahtarlarPF[i] = RSA.Create().ToXmlString(true)); // Süre: 00:00:07.3489338
var anahtarlarF = new string[600]; for (int i = 0; i < anahtarlarF.Length; i++) { anahtarlarF[i] = RSA.Create().ToXmlString(true); } // Süre: 00:00:28.7663622
ToString() metodunu dikkatli kullanın Özellikle döngüler içerisinde gereksiz tür değişimleri yapmayın. String türündeki bir değeri tekrar stringe dönüştürmek bedava değildir ve aşağıdaki örnekte olduğu gibi 40 kat daha yavaş çalışabilir.
private static Random fRandom = new Random();
private void Test() { string metin = string.Empty; for (int i = 0; i < 10000; i++) { int num = fRandom.Next(0, 26); metin += (char)('a' + num) + " "; }
Stopwatch sp1 = new Stopwatch(); sp1.Start(); try { int boşluklar1 = 0; for (int i = 0; i < metin.Length; i++) { if (metin[i] == ' ') // DOĞRU :) { boşluklar1++; } } } finally { sp1.Stop(); } Console.WriteLine("Süre: {0}", sp1.Elapsed); // Süre: 00:00:00.0000141
Stopwatch sp2 = new Stopwatch(); sp2.Start(); try { int boşluklar2 = 0; for (int i = 0; i < metin.Length; i++) { if (metin[i].ToString() == " ") // YANLIŞ :( { boşluklar2++; } } } finally { sp2.Stop(); } Console.WriteLine("Süre: {0}", sp2.Elapsed); //Süre: 00:00:00.0005905 }
Diğer öneriler For döngülerini tersine (azalan) şekilde çalıştırmak X86 tabanlı bazı işlemcilerde performans artışı sağlayabilir.
for (int i = metin.Length - 1; i >= 0; i--)
Eğer döngüde işiniz bittiyse döngüden çıkın.
List meyveler = new List(new string[] { "elma", "armut", "kiraz" }); int index = -1; for (int i = 0; i < meyveler.Count; i++) { if (meyveler[i] == "kiraz") { index = i; break; // Çıkın } }[b][/b]
Paylaşım için teşekkürler. Çoğu zaman göz ardı edilen, püf noktaları.
< Bu ileti tablet sürüm kullanılarak atıldı >
artık günümüz bilgisayarları ve configürasyon ları için sorun olmaz ama web tarafında sorun olabilir
Teşekkürler okumaya değer.
Faydalı bir yazı herkes tarafından bilinmesine rağmen dikkat edilmeyen unutulan gerçekler ama kullanılabilirlik açısından yararsız bi makale.
Projendeki tüm foreach'leri for yapsan değişen ne olacak? 1 milyonluk bi döngüde bile fark neredeyse yok. Görünürde 3 kat olsa da yok yani. 0.0000000000000000001 gibi bi rakam kimseye birşey katmaz. Kaldı ki gerçek hayatta kullanılan foreach'ler click başı ortalama 10-100-1000 arası gidip geliyor.
Günümüzde bu taktiklerin bir önemi kalmıyor malesef. Performans artık %99.9 database performansı. Konu performans olacaksa database sorgularını nasıl optimize edebiliriz, nasıl cache kullanırız vs. olmalı.
Zaten bunlar önemli olsa herkes dikkat edip projede foreach'leri for yapardı vs. Boş yere bunlar için kastırmaya gerek yok.