for schneller als foreach?



  • Ein erfahrener C#-Programmierer hat mir letztens erzählt das es performanter sei ein Array oder Objekt mit einer for-Schleife als mit einer foreach-Schleife zu durchlaufen. Ich bin allerdings der Meinung das der Compiler bei Beispiel 2 den gleichen oder eien genau so performanten IL-Code generiert wie bei Beispiel 1, da er ja theoretisch beim Kompelieren einfach alle foreach-Schleifen in for-Schleifen umwandeln könnte wenn diese performater seien.

    Was meint ihr?

    Beispiel 1

    int[] arr = { 1, 559, 3, 93, 4 };
    for (int i = 0; i < arr.Length; i++)
    {
    	Console.WriteLine(arr[i]);
    }
    

    Beispiel 2

    int[] arr = { 1, 559, 3, 93, 4 };
    foreach (number in arr)
    {
    	Console.WriteLine(number);
    }
    


  • ich habs zwar nicht gemessen und nicht unbedingt das interesse dran aber mal was anderes:

    wozu mit .Net programmieren wenn die Performance wichtig ist?



  • da in der zweiten Variante (zumindest) eine Referenz kopiert werden muss, muss foreach langsamer sein. Es sei denn, du erstellst ebenfalls eine Referenz in der for-Schleife. Dann ist es egal.

    wozu mit .Net programmieren wenn die Performance wichtig ist?

    1. Um ausgereiftere Frameworks benutzen zu können
    2. Um Webanwendungen genau wie WinForms-Anwendungen entwickeln zu können
    3. Um zukunftsfühig zu sein
    4. Um einfacher programmieren zu können



  • Online schrieb:

    ich habs zwar nicht gemessen und nicht unbedingt das interesse dran aber mal was anderes:

    wozu mit .Net programmieren wenn die Performance wichtig ist?

    Seit wann ist .Net nicht performant? Immer diese unbegründeten Sticheleien, zum abfackeln, echt... 🙄

    da in der zweiten Variante (zumindest) eine Referenz kopiert werden muss, muss foreach langsamer sein.

    Da würde ich nicht drauf wetten. Diese Referenz kann auch einfach nur vorgegaukelt sein und muss nicht zwangsläufig unnötigerweise kopiert werden.
    Da hier in diesem Beispiel sogar mit Valuetypes gearbeitet wird, gibt es gar keine Referenz. Ich kann mir nicht vorstellen, dass der jedes struct einfach kopiert, so wie ich mir auch generell nicht vorstellen kann, dass foreach langsamer sein soll.
    Ich glaube das nicht wirklich, habe jetzt aber auch keine Lust, da groß nachzuforschen.



  • AndreasW schrieb:

    wozu mit .Net programmieren wenn die Performance wichtig ist?

    1. Um ausgereiftere Frameworks benutzen zu können
    2. Um Webanwendungen genau wie WinForms-Anwendungen entwickeln zu können
    3. Um zukunftsfühig zu sein
    4. Um einfacher programmieren zu können

    ist mir alles klar ich benutze ja .Net selber, hat aber nix mit Performance zutun.

    Optimizer schrieb:

    Seit wann ist .Net nicht performant? Immer diese unbegründeten Sticheleien, zum abfackeln, echt... 🙄

    also unter Performance versteh ich das mir jeder takt wichtig ist. Wenn ich mit .Net programmiere, dann geht es mir bestimmt nicht darum 20 takte einzusparen.
    Wenn ich Zeitkritischen code (jeder takt ist wichtig) schreibe dann würde ich versuchen .Net zu umgehen (unsafe oder gleich C oder Assembler) weil es einfach dafür nicht ausgelegt ist.



  • > ....iert, so wie ich mir auch generell nicht vorstellen kann, dass foreach langsamer sein soll.

    schau:

    private void button1_Click(object sender, System.EventArgs e)
    		{
    			int max=50000000;
    
    			int count=100;
    			int[] source= new int[max];
    			int[] target= new int[max];
    			System.Random r=new Random();
    
    			for(int i=0;i<source.Length;++i)
    			{
    				source[i]=i;		
    			}
    
    			DateTime [] start1=new DateTime [count];
    			TimeSpan[] Diff1 =new TimeSpan[count];
    			DateTime [] start2=new DateTime [count];
    			TimeSpan[] Diff2 =new TimeSpan[count];		
    			for(int i=0;i<count;++i)
    			{
    				start1[i]=DateTime.Now;
    				for(int ii=0;ii<target.Length;ii++)
    				{
    					target[ii]=source[ii];
    				}
    				Diff1[i]=DateTime.Now.Subtract(start1[i]);
    				Console.WriteLine(Diff1[i].TotalMilliseconds);
    
    				start2[i]=DateTime.Now;
    				foreach(int val in source)
    				{
    					target[val]=val;
    				}
    				Diff2[i]=DateTime.Now.Subtract(start2[i]);
    				Console.WriteLine(Diff2[i].TotalMilliseconds);
    
    			}
    
    			double t1=0;
    			double t2=0;
    			foreach(TimeSpan s in Diff1)
    			{
    				t1+=s.TotalMilliseconds;
    			}
    			foreach(TimeSpan s in Diff2)
    			{
    				t2+=s.TotalMilliseconds;
    			}
    			t1=t1/count;
    			t2=t2/count;
    			Console.WriteLine("Durchschnitt for: "+ t1.ToString());
    			Console.WriteLine("Durchschnitt foreach: "+ t2.ToString());
    			Console.WriteLine("Geschwindigkeitsvorteil von for: {0}",((t2-t1)*count));
    		}
    


  • ich glaube aber nicht, dass das eine Umstellung auf for rechtfertigen würde.
    Bei einem Array mit 100 einträgen, glaube ich nicht, dass du einen Unterschied merkst.
    Bei großen Schleifen, insbesondere bei doppel-Scheifen (oder mehr), nehme ich meist for.



  • Ich nehme schon mal nicht ohne konkreten Grund das weniger schöne. Ich kann mit deinem Programm nicht viel anfangen, weil ich selber nur die 2005 beta habe und das in keinster Weise repräsentativ ist.
    Außerdem ist die Messung mit DateTime nicht aussagekräftig in einer inneren Schleife, weil das bei mir nicht mal in Millisekunden (!!) - Genauigkeit arbeitet. Und das in ner inneren Schleife.

    Ich schreib selber mal was kleines zam, aber wie gesagt, ich hab nur ne beta und glaube dann selber nicht an die Aussagekräftigkeit.

    P.S. ich hab das Ende deines Programms nicht abgewartet.

    EDIT: Ich halte die Beta Ergebnisse btw. deshalb nicht für Aussagekräftig, weil man damit nur Debug-Builds erstellen kann und keiner weiß, was der aus nem foreach jetzt macht.



  • ohne einen flamthread draus machen zu wollen... 😃

    man sollte in sachen performance nich allein ueber
    die "nackte" sprache nachdenken. nach dem motto:
    assembler < c < c++ < c#
    (c++ ist schneller als c#, c schneller als c++, ass schneller als c).

    das gilt wohl fuer kleine programme. grade bei grossen programmen
    laeuft man oft gefahr den ueberblick zu verlieren.
    dann mag eine einzelne anweisung geschrieben in assembler schneller
    sein. das "grosse ganze" assembler programm aber langsamer als
    ein vergleichbares in c++.

    grade bei c# denke ich, dass alles besser auf einander abgestimmt ist.
    das framework ist aus einem guss. bei c++ muesstest du um vergleichbare
    programme zu schreiben auf eine vielzahl unterschiedlicher libs
    zurueckgreifen. ob das ganze noch so gut zusammenlaeuft?

    solltest du dir mal gedanken drueber machen.

    bitte kein flame beginnen :). ich sage jetzt nicht c# ist besser
    als assembler. nur bitte nicht immer die aussagen der form:
    "mein assembler programm braucht fuer schleife x y rechentakte
    weniger -> alles wird schneller sein".



  • @Optimizer :
    das ergeibnis sieht so aus:

    .....
    500
    546,875
    484,375
    546,875
    500
    546,875
    484,375
    546,875
    500
    531,25
    500
    546,875
    500
    546,875
    484,375
    546,875
    500
    546,875
    484,375
    546,875
    500
    531,25
    500
    546,875
    484,375
    546,875
    500
    546,875
    500
    531,25
    515,625
    531,25
    500
    531,25
    500
    546,875
    500
    531,25
    515,625
    531,25
    500
    546,875
    500
    531,25
    500
    546,875
    500
    531,25
    500
    546,875
    500
    546,875
    500
    546,875
    484,375
    546,875
    484,375
    546,875
    500
    546,875
    484,375
    546,875
    500
    546,875
    500
    546,875
    484,375
    546,875
    484,375
    546,875
    500
    546,875
    500
    546,875
    484,375
    546,875
    500
    531,25
    500
    546,875
    500
    546,875
    500
    531,25
    500
    546,875
    484,375
    546,875
    500
    546,875
    500
    531,25
    500
    531,25
    500
    546,875
    500
    546,875
    500
    531,25
    500
    546,875
    500
    531,25
    500
    546,875
    484,375
    562,5
    500
    531,25
    Durchschnitt for: 513,59375
    Durchschnitt foreach: 567,96875
    Geschwindigkeitsvorteil von for: 5437,5
    

    Sicherlich birgt die Messmethode über DateTime Ungenauigkeiten. Diese Ungenauigkeiten gelten aber für beide (for und foreach). Deshalb habe ich die Mesungen auch so geschachtelt angeordnet. Das ist ein plausibler Vergleich. Du kannst mir erzählen was du willst. Das Ergebnis kannst du nicht wegdiskutieren.



  • Um mal was zum Thema beizutragen. Prinzipiell ist for immer schneller als foreach weil bei foreach ja auf alle Fälle Enumerator Objekte erstellt werden und auch benutzt werden. Und foreach wird meiner Meinung nach nie zu ner for Schleife optimiert(gerade aus dem Grund weil sie anders funktioniert), aber lass mich gerne was besseren belehren. Ist zwar Offtopic aber benutze auf Arbeit nen C Compiler von Tasking für nen TC1796(ist nen µC von Infineon) und was der optimiert ist unnormal, bin echt beeindruckt was der einen abnimmt an optimierungsarbeit 🙂 Um aber wieder zum Thema zurückzukommen, irgendwo hab ich auch mal nen Satz von nem Entwickler des C# Compilers gelesen das man nicht versuchen sollte den Compiler was vorabzugreifen an Optimierungen, des geht meist schief.



  • Sicherlich birgt die Messmethode über DateTime Ungenauigkeiten. Diese Ungenauigkeiten gelten aber für beide (for und foreach). Deshalb habe ich die Mesungen auch so geschachtelt angeordnet. Das ist ein plausibler Vergleich. Du kannst mir erzählen was du willst. Das Ergebnis kannst du nicht wegdiskutieren.

    Oh doch, das kann ich. Das die Ungenauigkeit für beide gilt, ist kein Argument. Der eine kann öfters 1,3ms pro Durchlauf kriegen (bei mir die kleinste Einheit), der andere öfters 0. Die Messung ist -sorry- Schrott.

    Außerdem sind diese Tests ziemlich witzlos aus ganz anderen Gründen noch. Ich habe gerade selber was geschrieben und was interessantes festgestellt.

    using System;
    using System.Runtime.InteropServices;
    
    class Test
    {
    	[DllImport("kernel32.dll")]
    	private extern static void QueryPerformanceCounter(out long currentTime);
    	[DllImport("kernel32.dll")]
    	private extern static void QueryPerformanceFrequency(out long frequency);
    
    	private static void Main()
    	{
    		int[] x = new int[100000000];
    
    		{
    		init(x);
    		int sum = 0;
    		long start, end;
    
    		QueryPerformanceCounter(out start);
    		foreach( int i in x )
    			sum += i;
    		QueryPerformanceCounter(out end);
    
    		Console.WriteLine("Summe 1: " + sum);
    		Console.WriteLine("Zeit 1: " + (end - start));
    		}
    
    		{
    		init(x);
    		int sum = 0;
    		long start, end;
    
    		QueryPerformanceCounter(out start);
    		for( int i = 0;  i < x.Length;  ++i )
    			sum += x[i];
    		QueryPerformanceCounter(out end);
    
    		Console.WriteLine("Summe 2: " + sum);
    		Console.WriteLine("Zeit 2: " + (end - start));
    		}
    
    		Console.In.Read();
    	}
    
    	private static void init(int[] x)
    	{
    		Random rand = new Random();
    
    		for( int i = 0;  i < x.Length;  ++i )
    			x[i] = rand.Next(50);
    	}
    }
    

    Nach meinem Ergebnis ist foreach fast 10% schneller. Aber wie aussagekräftig ist das. Man vertausche die Reihenfolge und auf einmal ist for 10% schneller. Volkard hat mal irgendwo erwähnt, dass das Betriebssystem "Langrechnern" irgendwann weniger Resourcen gibt.
    Vertausch mal bei deinem Test die Reihenfolge... würd mich interessieren. Aber die Messung kannst du eh vergessen über DateTime, du solltest dein Programm echt noch mal umschreiben.
    Jetzt haben wirs eh nur für valuetype-arrays getestet. Ein Szenario von tausend. Bei Ref-type arrays kanns wieder anders sein. Bei LinkedList wieder anders.

    Wir kriegen auf die Weise nix zusammen. Weder mit der Messung über DateTime noch mit dem (nicht-)umdrehen des Ergebnisses durch Umdrehen der Messreihenfolge.
    Ich pack mal, sobald 2005 final ist den Disassembler raus. Meine Theorie ist eh, dass beide Schleifen den selben Code generieren, wahrscheinlich schon auf MSIL-Ebene.



  • entelechie schrieb:

    ..
    man sollte in sachen performance nich allein ueber
    die "nackte" sprache nachdenken. nach dem motto:
    assembler < c < c++ < c#
    (c++ ist schneller als c#, c schneller als c++, ass schneller als c).

    das gilt wohl fuer kleine programme. grade bei grossen programmen
    laeuft man oft gefahr den ueberblick zu verlieren.
    dann mag eine einzelne anweisung geschrieben in assembler schneller
    sein. das "grosse ganze" assembler programm aber langsamer als
    ein vergleichbares in c++.

    grade bei c# denke ich, dass alles besser auf einander abgestimmt ist.
    das framework ist aus einem guss. bei c++ muesstest du um vergleichbare
    programme zu schreiben auf eine vielzahl unterschiedlicher libs
    zurueckgreifen. ob das ganze noch so gut zusammenlaeuft?

    solltest du dir mal gedanken drueber machen.

    bitte kein flame beginnen :). ich sage jetzt nicht c# ist besser
    als assembler. nur bitte nicht immer die aussagen der form:
    "mein assembler programm braucht fuer schleife x y rechentakte
    weniger -> alles wird schneller sein".

    natürlich...da wag ich dir nicht zu wiedersprechen. Ist alles zutreffend. Ich hab aber vom .Net gesprochen und nicht von C#.
    du kannst mit C# sehr wohl schnellen code shreiben aber dafür musst du mit unsafe .Net verlassen.
    Und wann braucht man schon wirklich schnellen code? Treiber vielleicht oder so...
    Die meisten Firmen sind froh das sie ihr Programm fertig kriegen. Da wird hier rumgebastellt und da geflickt. Von Perfomance kann keine rede sein. Das hängt auch ganz klar vom code ab.

    Irgendwo hab ich mal gelesen das man 20% Geschwindigkeitssteigerung braucht um körperlich einen Unterschied zu merken.



  • Talla schrieb:

    Um mal was zum Thema beizutragen. Prinzipiell ist for immer schneller als foreach weil bei foreach ja auf alle Fälle Enumerator Objekte erstellt werden und auch benutzt werden.

    Ähm ne. Hast du den Compiler geschrieben? Bei ner LinkedList brauchst du auch bei for nen Enumerator (außer du machst es mega-ineffizient über Index). Und bei einem Array wird foreach schon nichts anderes machen als for( int i = 0; i < array.Length; ++i )
    Falls das der Compiler nicht schaffen sollte, wäre es zu traurig, das würde ja sogar ich auf die Reihe kriegen, foreach nach for zu übersetzen.

    Und foreach wird meiner Meinung nach nie zu ner for Schleife optimiert(gerade aus dem Grund weil sie anders funktioniert),

    Warum ist das umwandeln zu einer for-Schleife "Optimierung" und inwiefern funktioniert eine vollständige Iteration über for anders als foreach?

    Wenn du mir 2min Zeit gibst, schreib ich dir ein Programm, dass foreach in for umwandelt.



  • natürlich...da wag ich dir nicht zu wiedersprechen. Ist alles zutreffend. Ich hab aber vom .Net gesprochen und nicht von C#.
    du kannst mit C# sehr wohl schnellen code shreiben aber dafür musst du mit unsafe .Net verlassen.

    Falsch. Um die zusätzliche Ausdrucksfähigkeit von unsafe Code zu deinem Vorteil nutzen zu können, musst du echt was drauf haben, das schaffen wir beide mit Sicherheit nicht.
    Ich unterstelle dir mal böse, dass in deinem Hinterkopf immer noch was mit "managed Code" in Zusammenhang mit "interpretieren" rumspukt?
    "managed Code" liegt als Native Code im RAM und ist auch genauso optimiert.
    Und noch eine winzige Kleinigkeit: du verlässt mit unsafe Code nicht .Net! unsafe Code ist immer noch managed Code und wird genauso JIT-compiliert. Unsafe Code ist nur nicht "veryfiable". Das ist ein ganz bedeutender Unterschied.

    Irgendwo hab ich mal gelesen das man 20% Geschwindigkeitssteigerung braucht um körperlich einen Unterschied zu merken.

    Ahhhhhhhhhhhhjaaaaaaaaaaa?! Ich kann nicht mal erraten, was das bedeuten soll.



  • Tach,
    hm,
    Ergebnis von deinem Programm:

    Summe 1: -1845137419
    Zeit 1: 103927728
    Summe 2: -1844790070
    Zeit 2: 2573163
    

    demnach ist die for-Schleife, wie ich schon sagte, schneller.



  • eigentlich muss man sagen....foreach wurde wohl für Array eingeführt...also warum auch nicht benutzen??? ist doch viel einfacher, übersichtlicher und auf einem WinXP Rechner mit 2GHz -> wieviel Elemente braucht wohl ein Array um mal einen Unterschied von 5 sec zwischen den beiden Schleifen zu spüren?



  • AndreasW schrieb:

    Tach,
    hm,
    Ergebnis von deinem Programm:

    Summe 1: -1845137419
    Zeit 1: 103.927.728
    Summe 2: -1844790070
    Zeit 2: 2.573.163
    

    demnach ist die for-Schleife, wie ich schon sagte, schneller.

    50mal schneller?
    Kann ich bei mir nicht nachvollziehen. Bei mir ist immer das erste schneller. Ich halte von diesem Test eh nichts (hab ich ja vorher schon angekündigt).
    Und du willst aber wohl auch nicht ernsthaft wegdiskutieren, dass es super einfach ist, ein foreach auf ein array in ein äquivalentes for umzuwandeln oder? Ich bin mir sicher, der Compiler generiert den selben Code.

    Und bei 50mal hast du dich jetzt bestimmt vertippt, oder?



  • Optimizer schrieb:

    natürlich...da wag ich dir nicht zu wiedersprechen. Ist alles zutreffend. Ich hab aber vom .Net gesprochen und nicht von C#.
    du kannst mit C# sehr wohl schnellen code shreiben aber dafür musst du mit unsafe .Net verlassen.

    Falsch. Um die zusätzliche Ausdrucksfähigkeit von unsafe Code zu deinem Vorteil nutzen zu können, musst du echt was drauf haben, das schaffen wir beide mit Sicherheit nicht.
    Ich unterstelle dir mal böse, dass in deinem Hinterkopf immer noch was mit "managed Code" in Zusammenhang mit "interpretieren" rumspukt?
    "managed Code" liegt als Native Code im RAM und ist auch genauso optimiert.
    Und noch eine winzige Kleinigkeit: du verlässt mit unsafe Code nicht .Net! unsafe Code ist immer noch managed Code und wird genauso JIT-compiliert. Unsafe Code ist nur nicht "veryfiable". Das ist ein ganz bedeutender Unterschied.

    ist klar, es ist ganz normaler MISL-Code aber trotzdem verläßt du .Net oder du gehst dran vorbei oder wie du das nennen willst. Im unsafe-Mode kannst du z.B. direkt auf den Speicher zugreifen. Und da musst du nun mal leider an .Net vorbei. Du gist deine Speicheranfragen dann nicht mehr weiter sondern direkt rein. Machst du da nen fehler haust du nicht nur dein Programm sondern auch das ganze .Net weg.

    Wenn du Hardwarenah programmieren willst dann kommst du an unsafe nicht vorbei, ist tatsache!

    Optimizer schrieb:

    Irgendwo hab ich mal gelesen das man 20% Geschwindigkeitssteigerung braucht um körperlich einen Unterschied zu merken.

    Ahhhhhhhhhhhhjaaaaaaaaaaa?! Ich kann nicht mal erraten, was das bedeuten soll.

    was verstehst du daran nicht? du fährst mit 80 km/h. Leichte geschwindigkeittsteigerung bemerkst du erst ab ca. 100. das mein ich.
    Hast du 2GHz CPU dann frühestens bei 2,400Ghz umsteigen. Weil du alles andere physisch nicht merkst.



  • oh, war wohl ein kopierfehler:

    hier eine andere Messung:

    Summe 1: -1845122201
    Zeit 1: 2777047
    Summe 2: -1844810424
    Zeit 2: 2553207
    

    und weil es so schön ist einmal umgekehrt:

    Summe 2: -1845073467
    Zeit 2: 2537453
    Summe 1: -1845054705
    Zeit 1: 2545909
    

    ist aber auch egal.
    Wir haben bei uns große Datenmengen im Umlauf. Wir müssen hier und da auch programmtechnisch Daten zusammenfügen, wobei wir nicht um doppelschleifen herumkommen. Meine Erfahrung hat gezeigt, das eine for-Schleife sich auch in der Praxis als schneller erweist.

    Mein Beispiel sollte ja nicht zeigen wieviel % for schneller ist, sondern das for tendentiell schneller ist. Das ist Fakt.


Anmelden zum Antworten