440 Stimmen

Welche Schleife in .NET läuft schneller, "for" oder "foreach"?

In C#/VB.NET/.NET, welche Schleife läuft schneller, for o foreach ?

Seitdem ich gelesen habe, dass ein for Schleife arbeitet schneller als eine foreach Schleife a vor langer Zeit Ich nahm an, dass dies für alle Sammlungen, generischen Sammlungen, alle Arrays usw. gilt.

Ich habe Google durchforstet und einige Artikel gefunden, aber die meisten davon sind nicht schlüssig (lesen Sie die Kommentare zu den Artikeln) und haben ein offenes Ende.

Ideal wäre es, jedes Szenario aufzulisten und die beste Lösung dafür zu nennen.

Ein Beispiel (nur ein Beispiel dafür, wie es sein sollte):

  1. für die Iteration eines Arrays von 1000+ Zeichenketten - for ist besser als foreach
  2. zur Iteration über IList (nicht generische) Zeichenketten - foreach ist besser als for

Ein paar Referenzen im Internet für das gleiche gefunden:

  1. Ursprünglicher großer alter Artikel von Emmanuel Schanzer
  2. CodeProject FOREACH Vs. FOR
  3. Blog - Zum foreach oder nicht foreach das ist die Frage
  4. ASP.NET Forum - NET 1.1 C# for vs foreach

[Bearbeiten]

Abgesehen vom Aspekt der Lesbarkeit bin ich wirklich an Fakten und Zahlen interessiert. Es gibt Anwendungen, bei denen es auf die letzte Meile der Leistungsoptimierung ankommt.

2voto

JohannesH Punkte 6310

Ich würde nicht erwarten, dass jemand einen "großen" Leistungsunterschied zwischen den beiden findet.

Ich schätze, die Antwort hängt davon ab, ob die Sammlung, auf die Sie zuzugreifen versuchen, eine schnellere Indexer-Zugriffsimplementierung oder eine schnellere IEnumerator-Zugriffsimplementierung hat. Da IEnumerator oft den Indexer verwendet und nur eine Kopie der aktuellen Indexposition hält, würde ich erwarten, dass der Enumerator-Zugriff mindestens so langsam oder langsamer als der direkte Indexzugriff ist, aber nicht viel.

Diese Antwort berücksichtigt natürlich nicht die Optimierungen, die der Compiler möglicherweise vornimmt.

1voto

mohamad tolou Punkte 9
    internal static void Test()
    {
        int LOOP_LENGTH = 10000000;
        Random random = new Random((int)DateTime.Now.ToFileTime());

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                for (int k = 0; k < dict.Count; k++)
                {
                    if (dict[k] > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict)
                {
                    if (item.Value > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }

        {
            Dictionary<int, int> dict = new Dictionary<int, int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i, i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict.Values)
                {
                    if (item > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"Dictionary foreach values T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }

        {
            List<int> dict = new List<int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                for (int k = 0; k < dict.Count; k++)
                {
                    if (dict[k] > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"list for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }

        {
            List<int> dict = new List<int>();
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();
            for (int i = 0; i < 64; i++)
            {
                dict.Add(i);
            }

            for (int i = 0; i < LOOP_LENGTH; i++)
            {
                foreach (var item in dict)
                {
                    if (item > 1000000) Console.WriteLine("Test");
                }
            }
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($"list foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");

            GC.Collect();
        }
    }

Wörterbuch für T:10.1957728s M:2080
Wörterbuch foreach T:10.5900586s M:1952
Wörterbuch foreach Werte T:3.8294776s M:2088
Liste für T:3.7981471s M:320
list foreach T:4.4861377s M:648

1voto

barlop Punkte 11355

Ich lief in einen Fall, wo foreach Weg WAY schneller als For

warum foreach beim Lesen von Richtextbox-Zeilen schneller ist als eine for-Schleife

Ich hatte einen ähnlichen Fall wie der OP in dieser Frage.

Ein Textfeld, das etwa 72K Zeilen liest, und ich habe auf die Eigenschaft Lines zugegriffen (die eigentlich eine Getter-Methode ist). (Und offenbar gibt es in Winforms oft Getter-Methoden, die nicht O(1) sind. Ich nehme an, es ist O(n), also je größer das Textfeld ist, desto länger dauert es, einen Wert von dieser "Eigenschaft" zu erhalten. Und in der for-Schleife, die ich als OP hatte, gab es for(int i=0;i<textBox1.lines.length;i++) str=textBox1.Lines[i] und es war wirklich ziemlich langsam, da es das gesamte Textfeld jedes Mal las, wenn es eine Zeile las, und es las das gesamte Textfeld jedes Mal, wenn es die Bedingung prüfte.

Jon Skeet zeigt, dass es möglich ist, nur ein einziges Mal auf die Eigenschaft Lines zuzugreifen (nicht einmal einmal pro Iteration, nur einmal). Anstatt zweimal bei jeder Iteration (was eine Unmenge von Malen ist). Do string[] strarrlines = textBox1.Lines; und Schleife durch die strarrlines.

Aber sicherlich ist eine for-Schleife in einer ziemlich intuitiven Form und der Zugriff auf die Eigenschaft Lines sehr ineffizient

for (int i = 0; i < richTextBox.Lines.Length; i++)
{
    s = richTextBox.Lines[i];
}

für ein Textfeld oder ein Rich-Textfeld, ist es super langsam.

Der OP, der diese Schleife auf einer Rich-Textbox testete, stellte fest, dass "bei 15000 Zeilen die for-Schleife 8 Minuten brauchte, um nur bis zu 15000 Zeilen zu schleifen. while foreach brauchte den Bruchteil einer Sekunde, um sie aufzuzählen."

Der OP in diesem Link fand diese foreach weitaus effizienter als seine (The same OP's) for-Schleife oben erwähnt werden. Wie ich es tat.

   String s=String.Empty;
   foreach(string str in txtText.Lines)
    {
       s=str;
    }

1voto

Anton Lyhin Punkte 1847

Ben Watson, der Autor von "Writing High-Performance .NET Code":

"Sind diese Optimierungen für Ihr Programm von Bedeutung? Nur wenn Ihr Programm CPU-gebunden ist und die Iteration der Auflistung ein Hauptbestandteil der Verarbeitung ist. Wie Sie sehen werden, gibt es Wege, wie Sie Ihre Leistung beeinträchtigen, wenn Sie nicht aufpassen, aber das ist nur wichtig, wenn es ein Teil Ihres Programms war. Meine Philosophie ist folgende: Die meisten Leute brauchen das nie zu wissen, aber wenn doch, dann ist es wichtig, jede Schicht des Systems zu verstehen. dann ist es wichtig, jede Schicht des Systems zu verstehen, damit man intelligente Entscheidungen zu treffen".

Die ausführlichste Erklärung finden Sie hier: http://www.codeproject.com/Articles/844781/Digging-Into-NET-Loop-Performance-Bounds-checking

1voto

GorillaApe Punkte 3501

In meinem Projekt in Windows Mobile habe ich eine for Schleife für eine Kontrollsammlung. Es dauerte 100 ms für 20 Kontrollen! A foreach Schleife nur 4 ms benötigt. Das war ein Leistungsproblem...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X