.NET'te hangi döngü daha hızlı çalışır, 'for' veya 'foreach'?


345

C # / VB.NET / .NET yılında hangi döngü daha hızlı çalışır, forya foreach?

forBir foreachdöngünün uzun zaman önce bir döngüden daha hızlı çalıştığını okuduğumdan beri , tüm koleksiyonlar, genel koleksiyonlar, tüm diziler vb. İçin doğru olduğunu varsaydım.

Google'ı inceledim ve birkaç makale buldum, ancak çoğu sonuçsuz (makaleler hakkındaki yorumları okuyun) ve açık uçlu.

İdeal olan, her senaryonun listelenmesi ve bunun için en iyi çözümdür.

Örneğin (nasıl olması gerektiğine dair bir örnek):

  1. 1000+ dize dizisi yineleme için - fordaha iyi olduğunuforeach
  2. üzerinden yineleme için IList(non jenerik) dizeleri - foreachdaha iyi olduğunufor

Web'de aynı için bulunan birkaç referans:

  1. Emmanuel Schanzer'in orijinal büyük eski makalesi
  2. CodeProject ÖNSÖZ Vs. İÇİN
  3. Blog - Sormak foreachya da istememek foreach, soru bu
  4. ASP.NET forumu - NET 1.1 C # forvsforeach

[Düzenle]

Okunabilirlik yönünün yanı sıra, gerçeklerle ve rakamlarla gerçekten ilgileniyorum. Sıkıştırılan son performans optimizasyonunun önemli olduğu uygulamalar vardır.


3
Fark hala var. Özellikle diziler her foreach altında hızlı olmalıdır, ancak diğer her şey için düz döngüler daha hızlıdır. Tabii ki, çoğu zaman, bu bir fark yaratmaz ve elbette, akıllı bir JIT derleyicisi teoride farkı ortadan kaldırabilir.
jalf

3
Bağlam olmadan, tam olarak ne yaptığınızı bilmiyorum, ancak kısmen dolu bir diziye rastladığınızda ne olur?
Chris Cudmore

6
Bu arada, ayda 2 milyon isabet korkutucu değil. Ortalama saniyede bir vuruştan daha az.
Mehrdad Afshari

37
Önemli Not : Bu soru dün C # foreachyerine kullanmaya zorlanma hakkında tamamen ilgisiz bir soru ile birleştirildi for. Burada hiç mantıklı olmayan cevaplar görürseniz, bu yüzden. Sorumsuz cevapları değil moderatörü suçla.
TED

7
@TED ​​Oh "patronun aptal olduğu" nereden geldiğini merak ediyordum, yorumlar
Gaspa79

Yanıtlar:


350

Patrick Smacchia , geçtiğimiz ay bu konuda aşağıdaki sonuçlarla blog yazdı :

  • Listedeki döngüler için List'deki foreach döngülerinden 2 kat daha ucuzdur.
  • Dizide döngü, Liste'de döngüden yaklaşık 2 kat daha ucuzdur.
  • Sonuç olarak, foreach kullanarak dizi döngü döngü foreach kullanarak (bu hepimizin ne yaptığına inanıyorum) kullanarak liste döngü 5 kat daha ucuzdur.

130
Ancak, asla unutma: "Erken optimizasyon tüm kötülüklerin köküdür."
Oorang

18
@Hardwareguy: Bunun neredeyse kabul edilemez derecede hızlı olduğunu öğrendikten sonra, neden genel olarak kullanmaya başlamıyorsunuz? Fazladan zaman almaz.
DevinB

47
@devinb, "for" kullanmak, "foreach" kodunu, başka bir değişkeni, kontrol etmeniz gereken bir durumu vb. eklediğinden "foreach" kullanmaktan daha zordur. ?
tster

35
@ Donanım yazılımı, bakalım bunu düzeltirim. Listede foreachdöngü yapmak, bir dizi üzerinde döngü yapmaktan daha uzun sürer forve bu önemsiz mi diyorsunuz? Bu tür bir performans farkı uygulamanız için önemli olabilir ve olmayabilir, ancak sadece elden çıkarmam.
Robert Harvey

44
Blog yazısı üzerinden okuma, testlerin Debug'da çalıştırıldığı ve Release değil çalıştırıldığı gibi görünüyor, bu yüzden bir faktör olabilir. Buna ek olarak, fark sadece döngü yükü için geçerlidir. Döngünün gövdesini yürütme süresini etkilemez, bu da çoğu durumda listenin bir sonraki öğesine geçmek için geçen süreden çok daha uzundur. Açıkça bir sorun olduğunu belirlediğinizde ve uygulamanızdaki farkı özellikle ölçtüğünüzde ve belirgin bir iyileşme olduğunu ancak tüm foreachs.
Davy8

165

İlk olarak, Dmitry'nin (şimdi silinmiş) cevabına karşı bir iddia . Diziler için, C # derleyicisi foreacheşdeğer bir fordöngü için olduğu gibi büyük oranda aynı kodu yayar . Bu, bu kriter için sonuçların temelde aynı olduğunu açıklıyor:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

Sonuçlar:

For loop: 16638
Foreach loop: 16529

Daha sonra, Greg'in koleksiyon türü hakkındaki amacının önemli olduğunu doğrulayın - diziyi List<double>yukarıdakinden bir olarak değiştirin ve radikal olarak farklı sonuçlar elde edin. Genel olarak sadece önemli ölçüde yavaş olmakla kalmaz, aynı zamanda foreach dizin tarafından erişime göre önemli ölçüde yavaşlar. Bunu söyledikten sonra, neredeyse her zaman kodun daha basit hale getirildiği bir for döngüsü tercih ediyorum - çünkü okunabilirlik neredeyse her zaman önemlidir, oysa mikro optimizasyon nadiren.


"diziyi yukarıdaki Listede <double> olarak değiştirin ve çok farklı sonuçlar elde edersiniz" Çok ilginç, hiç düşünmemiştim
johnc

5
Testlerim ve diğer insanların karşılaştırmaları arasındaki sonuçlardaki garip farklılıklar göz önüne alındığında, bunun bir blog gönderisini hak edeceğini düşünüyorum ...
Jon Skeet

1
Diziler arasında neredeyse her zaman hangisini tercih edersiniz List<T>? Okunabilirlik bu durumda mikro optimizasyonu da etkiliyor mu?
JohnB

12
@JohnB: Evet - neredeyse her zaman List<T>dizileri tercih ederim . İstisnalar vardır char[]ve byte[]hangi daha sık veri "parçalar" yerine normal bir koleksiyon olarak kabul edilir.
Jon Skeet

Şaşırtıcı, makinemde daha da agresif bir fark görüyorum, neredeyse% 10'u basit dizilerdeki her şeyden önce. Çılgınca bu tüm jitter kaynaklanıyor ekstra değişken, bağlı kontroller, vb hakkında endişelenmek zorunda değilsiniz .. birisi derin bir açıklama varsa çok ilginç olurdu tahmin ediyorum.
Tamir Daniely

162

foreachdöngüler daha özel amacı olduğunu fordöngüler .

Bir foreachdöngü kullanmak, kodunuzu kullanan herkese koleksiyondaki yeri ne olursa olsun bir koleksiyonun her üyesine bir şeyler yapmayı planladığınızı gösterir. Ayrıca orijinal koleksiyonu değiştirmediğinizi gösterir (ve denerseniz bir istisna atar).

Diğer bir avantajı ise foreach, sadece mantıklı IEnumerableolduğu gibi , her öğenin aslında bir indeksinin olduğu herhangi bir yerde çalışmasıdır .forIList

Ancak, bir öğenin dizinini kullanmanız gerekiyorsa, elbette bir fordöngü kullanmanıza izin verilmelidir . Ancak, bir dizin kullanmanız gerekmiyorsa, bir dizine sahip olmak sadece kodunuzu karmaşık hale getirir.

Farkında olduğum kadar önemli bir performans etkisi yok. Gelecekte bir aşamada, kodu foreachbirden fazla çekirdek üzerinde çalıştırmak için uyarlamak daha kolay olabilir , ancak şu anda endişelenecek bir şey değil.


23
ctford: Hayır, değil. Derleyici kesinlikle içindeki öğeleri yeniden sıralayamaz foreach. foreachişlevsel programlama ile hiç ilgili değildir. Bu tamamen zorunlu bir programlama paradigmasıdır. TPL ve PLINQ'da olan şeyleri yanlış ilişkilendiriyorsunuz foreach.
Mehrdad Afshari

13
@BlueTrin: Kesinlikle sipariş vermeyi garanti eder (C # spec bölüm 8.8.4 resmi foreacholarak bir whiledöngünün eşdeğeri olarak tanımlar ). Sanırım @ctford'ın bahsettiğini biliyorum. Görev paralel kitaplığı , temel alınan koleksiyonun öğeleri rastgele bir sırada ( .AsParallelnumaralandırılabilir bir numarayı çağırarak ) sağlamasına olanak tanır . foreachburada hiçbir şey yapmaz ve döngü gövdesi tek bir iş parçacığında yürütülür . Paralellik gösteren tek şey dizinin üretilmesidir.
Mehrdad Afshari

6
Enumerable.Select öğenin dizinini elde etmenizi sağlayan bir aşırı yüke sahiptir, bu nedenle bir dizine ihtiyaç bile kullanılmasını zorunlu kılmaz. Bkz. Msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill

5
ForEach, okunabilirlik ve yazma tasarrufu için kullanışlıdır. Maliyetler önemli olsa da; yaptığım bir belge tasarımcısı kullanıcı arabiriminde 2 veya 3 for-loop'u (listedeki her obj için) yerine (i = 0'dan list.count-1'e) değiştirerek yanıt süresini düzenleme başına 2-3 sn'den yaklaşık 2 saniyeye düşürdü. Sadece birkaç yüz nesne arasında döngü küçük bir belge üzerinde düzenleme başına 5 sn. Artık büyük belgeler için bile, hepsini döngüye sokma zamanı artmıyor. Bunun nasıl olduğu hakkında hiçbir fikrim yok. Ne biliyorum alternatif sadece nesnelerin bir alt kümesini döngü karmaşık bir şemasıdır. 5 dakikalık değişikliği her gün alacağım! - mikro optimizasyon değil .
FastAl

5
@FastAl Normal listeler arasındaki fark foreachve forperformans, milyonlarca öğeyi yinelemek için bir saniyenin kesiridir , bu nedenle sorununuz kesinlikle en az birkaç yüz nesne için doğrudan foreach performansıyla ilgili değildi. Kullandığınız listede ne olursa olsun kırık bir numaralandırma uygulaması gibi görünüyor.
Mike Marynowski

53

Performansla ilgili argümanlar olduğunda, vakanızı desteklemek için nicel sonuçlar kullanabilmeniz için küçük bir test yazmanız yeterlidir.

StopWatch sınıfını kullanın ve doğruluk için birkaç milyon kez tekrarlayın. (For döngüsü olmadan bu zor olabilir):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

Parmaklar, bu farkın ihmal edilebilir olduğunu gösterdi ve en sürdürülebilir kodda ne olursa olsun yapabilirsiniz.


13
Ancak foreach ve foreach performansını karşılaştıramazsınız. Farklı durumlarda kullanılması gerekiyor.
Michael Krelin - hacker

7
Sana katılıyorum Michael, performansa göre hangisini kullanacağını seçmemelisin - en anlamlı olanı seçmelisin! Ancak patronunuz "Öngörmeden daha yavaş olduğu için kullanma" diyorsa, bu farkın ihmal edilebilir olduğuna ikna etmenin tek yolu budur
Rob Fonseca-Ensor

"(Bu bir for döngüsü olmadan zor olabilir)" Veya bir while döngüsü kullanabilirsiniz.
jonescb

49

Her zaman yakın olacak. Bir dizi için, bazen for biraz daha hızlıdır, ancak foreachdaha ifade edicidir ve LINQ, vb. Sunar. Genel olarak, sopa ile foreach.

Ayrıca, foreachbazı senaryolarda optimize edilebilir. Örneğin, bağlantılı bir liste dizin oluşturucu tarafından korkunç olabilir, ancak hızlı olabilir foreach. Aslında, standart LinkedList<T>bu nedenle bir indeksleyici bile sunmuyor.


Yani LinkedList<T>daha zayıf olduğunu List<T>mu söylüyorsun ? Ve her zaman foreach(yerine for) kullanacaksam, kullanmam daha iyi olur LinkedList<T>mu?
JohnB

2
@JohnB - yalın değil; sadece farklı. Örneğin, bağlı listedeki her düğümün düz bir dizi için gerekli olmayan ek başvurular vardır (bu da temel oluşturur List<T>). Eklemek / çıkarmak daha ucuzdur .
Marc Gravell

36

Benim tahminim, vakaların% 99'unda muhtemelen önemli olmayacak, neden en uygun yerine daha hızlı (neden en kolay / bakımı kolay olduğu gibi) seçesiniz ki?


6
@klew, Gerçekten kodunuzu profillendirirseniz,% 20'sinin olabildiğince hızlı olması gerektiğini tahmin etmek zorunda kalmazsınız. Muhtemelen hızlı olması gereken gerçek döngü sayısının çok daha düşük olduğunu da öğreneceksiniz. Dahası, gerçekten döngü döngüsünün, o döngüde gerçekte yaptıklarınızın aksine, zamanınızı harcadığınız yer olduğunu mu söylüyorsunuz?
tster

32

İkisi arasında büyük bir performans farkı olması muhtemel değildir. Her zaman olduğu gibi, "hangisi daha hızlı?" soru, her zaman "Bunu ölçebilirim" diye düşünmelisin.

Döngünün gövdesinde aynı şeyi yapan iki döngü yazın, her ikisini de yürütün ve zamanlayın ve hızdaki farkın ne olduğunu görün. Bunu hem neredeyse boş bir gövdeyle hem de gerçekte ne yapacağınıza benzer bir döngü gövdesi ile yapın. Ayrıca, kullandığınız koleksiyon türüyle de deneyin, çünkü farklı koleksiyon türleri farklı performans özelliklerine sahip olabilir.


32

Döngülerin döngülere tercih edilmesinin çok iyi nedenleri vardır . Eğer bir döngü kullanabiliyorsanız , patronunuz tam size göre.foreachforforeach

Ancak, her yineleme tek tek sırayla bir listeden geçmez. Eğer yasaklıyorsa , evet bu yanlıştır.

Ben senin yerinde olsaydım, döngüler için tüm doğallarını özyinelemeye dönüştürürdüm . Bu ona öğretirdi ve aynı zamanda sizin için iyi bir zihinsel egzersiz.


Özyineleme, performans açısından fordöngüler ve foreachdöngülerle nasıl karşılaştırılır ?
JohnB

Değişir. Kuyruk özyineleme kullanırsanız ve derleyiciniz fark edecek kadar akıllıysa, aynı olabilir. OTOH: Değilse ve parametreler olarak çok sayıda gereksiz (değişmeyen) veri iletmek veya yığın üzerindeki büyük yapıları yerel olarak bildirmek gibi aptalca bir şey yaparsanız, gerçekten yavaş olabilir (hatta RAM tükenebilir).
TED

Ahhh. Neden şimdi sorduğunu anlıyorum. Bu cevap tamamen farklı bir soruya gitti. Bazı tuhaf nedenlerden dolayı Jonathan Sampson bu ikisini dün birleştirdi. Bunu gerçekten yapmamalıydı. Birleştirilen cevaplar burada hiçbir anlam ifade etmeyecektir.
TED

18

TechEd 2005'te Jeffrey Richter:

"Yıllar içinde C # derleyicisinin temelde bana yalancı olduğunu öğrenmek için geldim." .. "Pek çok şey hakkında yalan söylüyor." .. "Bir foreach döngüsü yaptığınızda olduğu gibi ..." .. "... bu yazdığınız küçük bir kod satırıdır, ama C # derleyicisinin bunu yapmak için tükürdüğü şey olağanüstüdür. deneyin / son olarak orada engelleyin, sonunda bloğun içinde değişkeninizi IDisposable bir arayüze çevirir ve cast alırsa üzerinde Dispose yöntemini çağırır, döngü içinde Current özelliğini ve MoveNext yöntemini döngü içinde tekrar tekrar çağırır, bir çok insan foreach kullanıyor, çünkü kodlaması çok kolay, yapması çok kolay .. ".." foreach performans açısından çok iyi değil,

İsteğe Bağlı Web Yayını: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US


12

Bu gülünç. For-loop'u, performans açısından veya diğerlerini yasaklamak için zorlayıcı bir neden yoktur.

Performans karşılaştırması ve diğer argümanlar için Jon Skeet'in bloguna bakın .



Daha hızlı olan döngü yapısı, neyi yinelemeniz gerektiğine bağlıdır. DataRows ve özel nesneler gibi birden çok nesne türü üzerinde birden çok yinelemeyi karşılaştıran başka bir blog . Ayrıca yalnızca for ve foreach döngü yapılarının değil, While döngü yapısının performansını da içerir.
Ücretsiz Kodlayıcı 24

11

Bir nesne koleksiyonuyla çalıştığınız durumlarda foreachdaha iyidir, ancak bir sayıyı arttırırsanız,for döngü daha iyidir.

Son durumda, aşağıdakine benzer bir şey yapabileceğinizi unutmayın:

foreach (int i in Enumerable.Range(1, 10))...

Ama kesinlikle daha iyi performans göstermiyor, aslında a'dan daha kötü bir performansa sahip for.


"Daha iyi" tartışmalı: Yavaş ve dnspy ​​hata ayıklayıcı bir C # foreach kırmak olmaz (VS2017 hata ayıklayıcı olacak rağmen). Bazen daha okunabilir ancak dili onsuz desteklerseniz sinir bozucu olabilir.
Zeek2

10

Bu sizi kurtarmalıdır:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

kullanın:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

Daha büyük kazanma için parametre olarak üç delege alabilirsiniz.


1
Küçük bir fark, foraralığın sonunu dışlamak için genellikle bir döngü yazılmasıdır (örn. 0 <= i < 10). Parallel.Forayrıca bunu ortak bir fordöngü ile kolayca değiştirilebilir tutmak için yapar .
Groo

9

Derin .NET - Bölüm 1 Yineleme hakkında okuyabilirsiniz

.NET kaynak kodunun sökülmesine kadar olan sonuçları (ilk başlatma olmadan) kapsar.

örneğin - Bir foreach döngüsü ile Dizi İterasyonu: resim açıklamasını buraya girin

ve - foreach döngüsü ile yinelemeyi listeleyin: resim açıklamasını buraya girin

ve sonuç: resim açıklamasını buraya girin

resim açıklamasını buraya girin


8

Bir de hız farklılıkları for- ve bir foreachsen vb diziler, listeler, gibi ortak yapılar aracılığıyla döngü ve a yapıyoruz küçük -loopLINQ ve koleksiyon üzerinde sorgu neredeyse her zaman biraz daha yavaştır, ancak yazmak daha iyidir! Diğer posterlerin de söylediği gibi, milisaniye ekstra performanstan ziyade ifadeyi tercih edin.

Şimdiye kadar söylenmeyen şey, bir foreachdöngü derlendiğinde, yinelemekte olduğu koleksiyona göre derleyici tarafından optimize edilmesidir. Bu, hangi döngüyü kullanacağınızdan emin olmadığınız zaman, foreachdöngüyü kullanmanız gerektiği anlamına gelir - derlendiğinde sizin için en iyi döngüyü üretecektir. Daha okunabilir.

foreachDöngü ile ilgili bir diğer önemli avantaj , toplama uygulamanız değiştiğinde ( örneğin bir int'den arraybir List<int>örneğe), foreachdöngünüzün herhangi bir kod değişikliği gerektirmemesidir:

foreach (int i in myCollection)

Yukarıdakiler, koleksiyonunuzun türü ne olursa olsun aynıdır, halbuki fordöngünüzde, ' myCollectionden' arraye değiştirirseniz aşağıdakiler oluşmaz List:

for (int i = 0; i < myCollection.Length, i++)

7

"Onu for döngüsünün kabul edilebilir olduğuna ikna etmeme yardımcı olacak herhangi bir argüman var mı?"

Hayır, patronunuz size hangi programlama dilini kullanacağınızı söyleyecek düzeyde mikro yönetim yapıyorsa, söyleyebileceğiniz hiçbir şey yoktur. Afedersiniz.


7

Muhtemelen numaralandırdığınız koleksiyonun türüne ve dizinleyicisinin uygulanmasına bağlıdır. Bununla birlikte, genel foreacholarak, kullanımın daha iyi bir yaklaşım olması muhtemeldir.

Ayrıca, IEnumerableyalnızca dizin oluşturucularla değil , herhangi biriyle çalışır .


7

Bu, çoğu "daha hızlı" sorusuyla aynı iki cevaba sahiptir:

1) Ölçmezseniz, bilmiyorsunuz.

2) (Çünkü ...) Duruma göre değişir.

Bu yineleme yapacağınız IEnumerable tipi (veya türleri) için "MoveNext ()" yönteminin ne kadar pahalı olduğuna, "this [int index]" yönteminin ne kadar pahalı olduğuna bağlıdır.

"Foreach" anahtar sözcüğü bir dizi işlem için kısayoldur - IEnumerable'da bir kez GetEnumerator () öğesini çağırır, yineleme başına bir kez MoveNext () öğesini çağırır, bazı tür denetimleri yapar, vb. Performans ölçümlerini etkilemesi en muhtemel şey MoveNext () 'in maliyetidir, çünkü O (N) kez çağrılır. Belki ucuz, ama belki de değil.

"For" anahtar kelimesi daha öngörülebilir görünüyor, ancak döngülerin çoğu için "toplama [dizin]" gibi bir şey bulacaksınız. Bu basit bir dizi indeksleme işlemine benziyor, ancak aslında maliyeti tamamen yinelediğiniz koleksiyonun doğasına bağlı olan bir yöntem çağrısı. Muhtemelen ucuz, ama belki de değil.

Koleksiyonun temeldeki yapısı temelde bağlantılı bir listeyse, MoveNext kir ucuzdur, ancak dizinleyicinin O (N) maliyeti olabilir ve bu da bir "for" döngüsü O (N * N) 'nın gerçek maliyetini oluşturur.


6

Her dil yapısının kullanım için uygun bir zamanı ve yeri vardır. C # dilinin dört ayrı yineleme ifadesine sahip olmasının bir nedeni vardır - her biri belirli bir amaç için vardır ve uygun bir kullanıma sahiptir.

Patronunuzla oturmanızı ve forilmiğin neden bir amacı olduğunu rasyonel olarak açıklamaya çalışmanızı öneririm . Bir foryineleme bloğunun bir algoritmayı bir foreachyinelemeden daha açık bir şekilde tanımladığı zamanlar vardır . Bu doğru olduğunda, bunları kullanmak uygundur.

Ayrıca patronunuza da dikkat çekerim - Performans değildir ve pratik bir şekilde bir sorun olmamalıdır - daha çok algoritmayı özlü, anlamlı, sürdürülebilir bir şekilde ifade etme meselesidir. Bunun gibi mikro optimizasyonlar, performans optimizasyonunun noktasını tamamen özlüyor, çünkü herhangi bir gerçek performans avantajı, döngü yeniden yapılandırmasından değil algoritmik yeniden tasarım ve yeniden düzenleme işlemlerinden gelecek.

Rasyonel bir tartışmadan sonra hala bu otoriter görüş varsa, nasıl ilerleyeceğiniz size bağlıdır. Şahsen, rasyonel düşüncenin cesaretinin kırıldığı bir ortamda çalışmaktan mutlu olmazdım ve farklı bir işveren altında başka bir konuma geçmeyi düşünürdüm. Ancak, üzülmeden önce tartışmayı şiddetle tavsiye ederim - yerinde basit bir yanlış anlama olabilir.


5

Yaptığın ne içeride perfomance etkileyen döngü değil gerçek döngü yapısı (davanızı varsayarak Önemsiz olmayan).


5

Gerçekte forolduğundan daha hızlı olup olmadığı daha foreachönemlidir. Birinden diğerinin seçilmesinin performansınız üzerinde önemli bir etki yaratacağından şüpheliyim.

Uygulamanızı optimize etmenin en iyi yolu, gerçek kodun profillenmesidir. Bu, en çok iş / zamanı açıklayan yöntemleri belirler. Önce bunları optimize edin. Performans hala kabul edilebilir değilse, prosedürü tekrarlayın.

Genel bir kural olarak, nadiren önemli kazanımlar sağlayacakları için mikro optimizasyonlardan uzak durmanızı tavsiye ederim. Tek istisna, tanımlanmış etkin yolları optimize etmektir (örn. Profilleriniz çok kullanılan birkaç yöntemi tanımlarsa, bunları kapsamlı bir şekilde optimize etmek mantıklı olabilir).


Üzerinde çalıştığım projelerde yapmam gereken tek optimizasyon mikro optimizasyon olsaydı, mutlu bir kampçı olurdum. Ne yazık ki, bu asla böyle değil.
Yannick Motton

2
formarjinal olarak daha hızlıdır foreach. Bu ifadeye ciddiyetle itiraz ederim. Bu tamamen temeldeki koleksiyona bağlıdır. Bağlantılı bir liste sınıfı bir tamsayı parametresi ile bir dizinleyici sağlarsa, forO (n) foreacholması beklenirken bir döngü O (n ^ 2) olması beklenir.
Mehrdad Afshari

@Merhdad: Aslında bu iyi bir nokta. Sadece bir liste (yani dizi) indeksleme normal vaka hakkında düşünüyordum. Bunu yansıtmak için yeniden yazacağım. Teşekkürler.
Brian Rasmussen

@Mehrdad Afshari: Bir koleksiyonu tamsayı ile dizine eklemek , numaralandırılmasından çok daha yavaş olabilir . Ancak aslında kullanımı for ve endeksleyici aramasını foreachtek başına kullanmayı karşılaştırıyorsunuz . @Brian Rasmussen'in cevabı, bir koleksiyonla herhangi bir kullanım dışında, her forzamankinden biraz daha hızlı olacağının doğru olduğunu düşünüyorum foreach. Bununla birlikte, forbir koleksiyon araması her zaman foreachkendisinden daha yavaş olacaktır .
Daniel Pryden

@Daniel: Ya her ikisi de aynı kodu üretecek düz bir diziniz var ya da fordeyimi kullandığınızda bir dizinleyici var . forTamsayı kontrol değişkenli düz döngü ile karşılaştırılamaz foreach, bu yüzden dışarıda. @Brian'ın ne anlama geldiğini anlıyorum ve dediğin gibi doğru ama cevap yanıltıcı olabilir. Re: Son nokta: Hayır, aslında forüzerinde List<T>daha hızlı hala foreach.
Mehrdad Afshari

4

İkisi neredeyse aynı şekilde koşacak. Her ikisini de kullanmak için bir kod yazın, sonra ona IL'yi gösterin. Karşılaştırılabilir hesaplamaları göstermelidir, yani performansta bir fark yoktur.


Derleyici, dizilerde / IListlerde vb. Kullanılan her foreach döngüsünü tanır ve bunları döngüler için olarak değiştirir.
Callum Rogers

3
Ona anlaşılmaz bir kanıt çizgisi gösterin ve sorun olmadığına dair kanıt isteyin.
cjk

3

Çünkü uygulamak için daha basit bir mantık vardır, bu yüzden foreach'den daha hızlıdır.


3

Belirli bir hız optimizasyonu sürecinde değilseniz, hangi yöntemi okumanın ve sürdürmenin en kolay yoldan hangisini kullandığını söyleyebilirim.

Koleksiyon sınıflarından birinde olduğu gibi bir yineleyici ayarlanmışsa, foreach iyi bir kolay seçenektir. Ve yinelediğiniz bir tamsayı aralığıysa, muhtemelen daha temizdir.



3

Çoğu durumda gerçekten hiçbir fark yoktur.

Genellikle, her zaman açık bir sayısal dizininiz olmadığında her zaman foreach kullanmak zorundasınız ve her zaman yinelenebilir bir koleksiyonunuz olmadığında kullanmak zorundasınız (örneğin, bir üst üçgendeki iki boyutlu dizi ızgarası üzerinde yineleme) . Seçim yapabileceğiniz bazı durumlar var.

Kodda sihirli sayılar görünmeye başlarsa döngüler için bakımı biraz daha zor olabilir. For döngüsünü kullanamayacağınız için rahatsız olmanız ve sadece döngüler yasaklandığı için bir alt koleksiyon oluşturmak için bir koleksiyon veya lambda kullanmanız gerekir.


3

For döngüsü gibi bir şeyin kullanımını tamamen yasaklamak biraz garip görünüyor.

Burada iki döngü arasındaki performans farklılıklarının çoğunu kapsayan ilginç bir makale var .

Şahsen ben döngüler için biraz daha okunabilir foreach buluyorum ama eldeki iş için en iyi kullanmak gerekir ve bir for döngüsü daha uygun ise bir foreach döngü eklemek için ekstra uzun kod yazmak zorunda değilsiniz.


Bağlantı verdiğiniz makaleden gerekli alıntı: "... koleksiyonlar için olmayan yüksek performanslı kod yazmayı planlıyorsanız, döngü için kullanın. Koleksiyonlar için bile, foreach kullanırken kullanışlı görünebilir, ancak o kadar verimli değil."
NickFitz

3

Daha hızlıforeach yinelenen döngüyü buldum . Aşağıdaki test sonuçlarıma bakın. Aşağıdaki kodda , zamanı ölçmek için ve döngüsünü kullanarak ayrı ayrı 100, 10000 ve 100000 boyutlarında bir yineleme yapıyorum .List arrayforforeach

resim açıklamasını buraya girin

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

GÜNCELLENMİŞ

@Jgauffin önerisinden sonra @johnskeet kodunu kullandım ve fordöngüsünün arrayaşağıdakilerden daha hızlı olduğunu gördüm ,

  • Dizi ile foreach döngüsü.
  • Listeli döngü için.
  • Listeli foreach döngüsü.

Test sonuçlarımı ve kodumu aşağıya bakın,

resim açıklamasını buraya girin

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }

3
Bu çok kötü bir test. a) kesin bir cevap almak için çok az yineleme yaparsınız b) Bu Thread.Sleep gerçekten bir milisaniye beklemeyecektir. Yanıtında Jon Skeet ile aynı yöntemi kullanın.
jgauffin

1
Zamanın% 99,99'u kesinlikle thread.sleep içinde geçirilir (en azından o zamandan önce olmayacak dışında ne kadar hızlı döneceği konusunda hiçbir garanti vermez). Döngü çok hızlı ve uyku çok yavaş, daha sonra ilkini test etmek için kullanmıyorsunuz.
Ronan Thibaudau

3

Gerçekten kafasını vidalayabilir ve bunun yerine IQueryable. Foreach kapatma için gidebilirsiniz:

myList.ForEach(c => Console.WriteLine(c.ToString());

3
Kod satırınızı onunla değiştirirdim myList.ForEach(Console.WriteLine).
Mehrdad Afshari

2

Kimsenin ikisi arasında "büyük" bir performans farkı bulmasını beklemezdim.

Cevap, erişmeye çalıştığınız koleksiyonun daha hızlı bir dizinleyici erişim uygulamasına veya daha hızlı bir IEnumerator erişim uygulamasına sahip olup olmadığına bağlıdır. IEnumerator genellikle dizinleyiciyi kullandığından ve yalnızca geçerli dizin konumunun bir kopyasına sahip olduğundan, numaralandırıcı erişiminin doğrudan dizin erişiminden en az yavaş veya daha yavaş olmasını beklerim, ancak çok fazla değil.

Elbette bu cevap, derleyicinin uygulayabileceği optimizasyonları hesaba katmaz.


C # derleyicisi çok az optimizasyon yapar, bunu gerçekten JITter'a bırakır.
ljs

JITter bir derleyici ... Değil mi?
JohannesH

2

For-loop ve foreach-loop'un her zaman eşdeğer olmadığını unutmayın. Liste değişirse liste numaralandırıcıları bir istisna atar, ancak bu uyarıyı her zaman normal bir döngü için alamazsınız. Liste yanlış zamanda değiştirilirse farklı bir istisna bile alabilirsiniz.


Liste altınızdan değişiyorsa, bunu söylemek için bir foreach döngüsünü destekleyen numaralandırıcıya güvenemezsiniz. Değeri size döndürdükten sonra tekrar kontrol etmeyecek ve bir yarışa neden olacaktır.
hoodaticus
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.