for schneller als foreach?



  • 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.



  • 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.

    Nene, das ist ziemlicher Unsinn. Du verlässt .Net mit unsafe Code nicht, das ist Fakt.

    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!

    Das ist doch Blödsinn. Was heißt bitte "direkt" auf den Speicher zugreifen??
    Du kannst die Adresse holen toll, das darfst du halt sonst einfach nur aus Sprach-Gründen nicht (weil der GC Objekte verschiebt und weil mit Addressen zu rechnen schief gehen kann).
    Da ist nix mit "direkt auf den Speicher zugreifen". Du kannst im veryfiable Code nur keine Fehler machen, im unsafe Code schon. Außerdem hast du im veryfiable Code auf dem managed Heap eine Indirektion mehr. Und das ist alles. Da sind keine Checks drin oder sonst was, alleine die Sprache stellt sicher, dass du mit den Addressen keine Fehler machen kannst.

    Wenn du im veryfiable Code fehler machen könntest (wenn die Sprachmittel das zuließen), würdest du auch "das ganze .Net weghauen", was wohl soviel heißen soll wie, dass du eine acces violation kriegst.

    Das hat auch mit hardwarenah programmieren nicht wirklich was zu tun, genauso wie C++ nicht wirklich hardwarenah ist. Nen Speicher hat jeder Computer, unsafe Code ist kaum mehr low-level als managed Code, du hast nur weniger Prüfungen und keinen GC.

    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.

    Was hat das mit dem Programm zu tun? Wieso musss mein Programm 20% schneller werden, damit ich was merke? Davon abgesehen sind so generelle Aussagen wie "Programm x ist 20% schneller als Programm y" eh nicht tragbar. Da muss man eine bestimmte Aufgabe spezifizieren, und und und. In der innersten und kritischsten Schleife sind 20% definitiv nicht tragbar.



  • @AndreasW: Kennst du die Messeinheit? Auf meinem Prozessor ungefähr ne 4millionstel Sekunde... Das kann an so vielen Faktoren liegen. Wie gesagt, ich zweifle mein Programm selber an, aber zumindest sehe ich eher die Tendenz, dass die Teile gleich schnell sind. Von "fakt, dass for schneller ist" kann man IMHO hier nicht sprechen.

    Aber ist ja auch egal. Wir wissen ja beide, dass die Performance eines Programms nicht an einer for(each) Schleife hängt.
    Ich würde aber grundsätzlich foreach nehmen und nur wenn es sich echt als Flaschenhals erweist, testen ob for schneller ist. Du darfst auch nicht vergessen, dass wir hier einen _sehr_ speziellen und beschränkten Fall hier betrachtet haben.



  • Kennst du die Messeinheit? Auf meinem Prozessor ungefähr ne 4millionstel Sekunde...

    joh, kenn ich von c++.

    Das kann an so vielen Faktoren liegen

    sicherlich.

    Aber ist ja auch egal. Wir wissen ja beide, dass die Performance eines Programms nicht an einer for(each) Schleife hängt.

    klar, wer schleifen vermeiden kann, sollte das tun. Wenn man bei einem so großen array mit einer Schleife ran muss, würde ich das array eh partitionieren, sofern man nicht alle Werte benötigt.

    Ich würde aber grundsätzlich foreach nehmen und nur wenn es sich echt als Flaschenhals erweist, testen ob for schneller ist.

    naja, für mich gibt es da ein Kriterium mehr. Wenn ich den Index eines Wertes brauche oder Items aus einem Array lösche oder einfüge, würde ich immer eine for-Schleife nehmen.
    Wenn ich nicht auf den Index angewiesen bin und vielleicht nur ein Array auslesen will, nehme ich foreach. Ist leserlicher.

    Aber das kann ja auch Geschmackssache sein...



  • Nene, das ist ziemlicher Unsinn. Du verlässt .Net mit unsafe Code nicht, das ist Fakt.

    hauptfunktion von .Net -> Die Resoucen vom System zur verfügung stellen.
    Besorge ich mir z.B. Speicher besorgen ich mir somit selber Resoucen. darum gibts ja auch z.B. das schlüsselwort fixed damit .Net nicht unerlaubt Daten im Speicher verschiebt auf die du vielleicht in unsafe zugreifen möchtest.
    und für mich ist das .Net verlassen weil .Net "nicht mehr weis" was du tust.

    aber da kann man ja auch natürlich drüberstreiten

    auch was hardwarenach ist kann man drüber streiten. Tatsache ist das MS selbst sagt: unsafe bei Leistungskritischer Code

    Was hat das mit dem Programm zu tun? Wieso musss mein Programm 20% schneller werden, damit ich was merke? Davon abgesehen sind so generelle Aussagen wie "Programm x ist 20% schneller als Programm y" eh nicht tragbar. Da muss man eine bestimmte Aufgabe spezifizieren, und und und. In der innersten und kritischsten Schleife sind 20% definitiv nicht tragbar.

    weil der Mensch zu langsam ist um kleine geschwindigkeitsunterschiede zu merken. Die meisten Programme verbringen doch sowieso mehr Zeit damit auf den User zu warten als was zu tun.

    übrigens hab ich nicht gesagt das die schleife 20% langsammer sein muss...welche schleife auch immer.
    ich hab geschrieben das du schon 20% leistungsunterschied brauchst um was zu merken.
    Jetzt vergleich das aber nicht mit Videoencoding oder sowas -> das hat nix mehr mit dem Menschen zutun, da gehts um reine Rechenpower.



  • auch was hardwarenach ist kann man drüber streiten. Tatsache ist das MS selbst sagt: unsafe bei Leistungskritischer Code

    Microsoft sagt aber nicht, dass man dazu auch was drauf haben muss, um das dann besser zu machen als der Compiler und der GC.
    Es gibt dort viele Fallstricke, vielleicht übersieht man Optimierungen, die der JITer bei veryfiable Code ohne zu zögern gemacht hätte, sich aber hier nicht traut.
    Vielleicht wird alles noch langsamer, weil das Allokieren auf dem unmanaged Heap wesentlich langsamer ist? Vielleicht hätte es der managed Heap besser gemacht?

    Nene, da können wir beide nicht viel mit rausholen, aber ich weiß das wenigstens noch, mit dem Gedanken solltest du dich auch mal anfreunden. Das heißt ja nicht, dass ich es nicht mal probieren würde, wenn ich ganz genau Bescheid wüsste, dass eine kleine Drecksfunktion mir die ganze Performance ruiniert, aber so extrem ist mir das noch nicht untergekommen.

    Grundlos nutze ich das nicht, denn man zahlt dafür definitiv auch seinen Preis (z.B. benötigt man eine höhere Sicherheitserlaubnis, um den Code auszuführen).

    Und ich bleibe definitiv dabei, dass man .Net nicht damit verlässt.

    hauptfunktion von .Net -> Die Resoucen vom System zur verfügung stellen.
    Besorge ich mir z.B. Speicher besorgen ich mir somit selber Resoucen.

    Nein, wie kommst du denn da drauf? Du benutzt immer noch nen Allokator von .Net mit fixed benutzt du halt einen, der dir auf dem unmanaged Heap Speicher zur Verfügung stellt.

    weil der Mensch zu langsam ist um kleine geschwindigkeitsunterschiede zu merken. Die meisten Programme verbringen doch sowieso mehr Zeit damit auf den User zu warten als was zu tun.

    übrigens hab ich nicht gesagt das die schleife 20% langsammer sein muss...welche schleife auch immer.
    ich hab geschrieben das du schon 20% leistungsunterschied brauchst um was zu merken.
    Jetzt vergleich das aber nicht mit Videoencoding oder sowas -> das hat nix mehr mit dem Menschen zutun, da gehts um reine Rechenpower.

    Das ist doch ne Milchmädchenaussage. Nimm mir 20% Frames von FarCry weg und ich werd sicher nicht sagen, dass ich keinen Unterschied merke. Du nennst viel zu leichtfertig irgendwelche konkreten Zahlen, das ist nicht sehr sinnvoll.



  • Ich habe die Geschwindigkeit jetzt auch mal getestet, allerdings nur mit DateTime und bei mir war die for-Schleife ca. doppelt so schnell wie die foreach und beim Test mit unsafe gab es überhaupt keine Abweichung zu den Tests ohne unsafe.



  • Wie sieht es eigentlich bei Enums aus? Mein Versuch ein Enum mit einer for-Schleife zu durchlaufen ist wesentlich langsamer als die Alternative mit der foreach-Schleife, aber vielleicht lag es auch nur an meinem Ansatz.

    class Class1
    {
    	private enum Chars { a = 97, b, c, d, e, f ,g ,h ,i ,j , k, l ,m ,n ,o ,p ,q ,r ,s ,t, u, v, w, x, y, z,
    							 A = 65, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
    							 zero = 48, one, two, three, four, five, six, seven, eight, nine,
    							 ae = 228, oe = 246, ue = 252, AE = 196, OE = 214, UE = 220, sz = 223,
    							 space = 32, placeholder = 95, hyphen = 45, nothing = 0 };
    
    	[STAThread]
    	static void Main(string[] args)
    	{
    		for (int char1 = (int) Chars.nothing; char1 <= (int) Chars.sz; char1++)
    		{
    			// ...
    		}
    	}
    }
    


  • wie gesagt, bei solch kleine Schleifen macht es überhaupt keinen Sinn überhaupt darüber nachzudenken.



  • Das war doch jetzt nur ein Vereinfachtes Beispiel. Außerdem steht da nicht umsonnst "//...". Die Schleife kann durchaus so verschachtelt werden das sie mehrere Minuten oder sogar Stunden brauchen kann. Also hat jemand ne Idee wie man das beschleunigen könnte?



  • Es zwingt doch niemand, foreach zu nutzen. Man hat die Wahl. 😉



  • hm, wenn du alle Daten in einer Datenmenge anfassen musst, kommst du nicht um die vollständige Iteration durch die Datenmenge herum.

    Angenommen, du musst nicht jeden Wert in der Datenmenge ändern. Dann müsstest du, wenn du mit Schleifen arbeitest, alle Daten durchgehen , obwohl du eigentlich nicht alle brauchst. Je weniger Daten in einer Datenmenge verändert werden müssen und desto größer die Datenmenge wird, desto unwirtschaftlicher wird eine Schleifenlösung. Du kannst dann eine andere Datenhaltung in Erwägung ziehen, um die Verarbeitungsgeschwindigkeit zu erhöhen.

    Dann hast du zum Beispiel folgende Möglichkeiten:

    1. Möglichkeit
    Dann würde ich die Daten in einem Baum (hierarchisch ) umbauen, wenn es die Daten zulassen. Ein Baum hat den Vorteil, dass das finden von Daten natürlich wesentlich schneller ist. Man (ich sag einfach mal) "partitioniert" die Daten anhand der Datendimensionen. Wenn man ein entsprechendes Klassendesign aufsetzt, kann man die Daten einfach reukursive Methoden schreiben, die sich durch den Baum hoch und runter hangeln, um Daten einzusammeln oder zu verändern.
    Der Geschwindigkeitsvorteil hängt von der Strukturierung des Baums ab.
    Diese Variante hat den Vorteil, da man die Verarbeitungslogik in der BaumItem-Klasse implementieren kann. Auuserdem ist es einem Baum egal, wieviele Dimensionen es gibt. Bäume sind dann praktisch, wenn die Anzahl der Dimensionen sich verändern können und/oder man Datenmanipulation auf aggregierten Datenebenen vornehmen möchte.

    2. Möglichkeit
    Eine andere Lösung ist das Sortieren der Daten und anschließendes "merken" von Schlüsselpositionen. Zum Beispiel eine Sortierung eines alphabetische Sortierung eines Arrays und Speicherung der Position des ersten Datensatzes eines Buchstaben. Diesen "Index" kann man dann zur schnellen Lokalisierung des Sucheintrittspunktes nutzen. Man braucht nicht durch das ganze Array.
    Diese Variante hat den Vorteil, dass man ein vorhandenes Array nicht neu aufbauen muss (was ja auch Zeit kosten)

    3. Möglichkeit
    Eine weitere Lösung ist das speichern der Daten in mehrdimensionale Arrays, wobei die Daten inhaltlich gruppiert in den Dimensionen abgelegt werden.
    Diese Variante ist die schnellste weil eine Iteration über die Ebenen nicht notwendig ist. Hat aber den Nachteil, dass die Anzahl der Dimensionen feststehen muss und nicht Variabel sein kann (zumindest nicht mit vertretbaren Aufwand).

    Berücksichtige dabei:

    - Das bei großen Datenmengen die Datenhaltung wichtig ist. Wenn du die Datenhaltung selbst beeinflussen kannst, solltest du eine der obrigen Varianten benutzen oder dir selber etwas anderes oder eine Mischform erarbeiten. Wenn du die Generierung der Daten nicht beeinflussen kannst, musst du entscheiden, um eine Umstrukturierung der Daten nach dem Laden eine Option ist. Wenn nicht, solltest du die 2.Möglichkeit in Erwägung ziehen.

    - Dass das Speichern von geänderten Datenmengen vielleicht auch wieder Restriktionen der Datenstrukturen mit sich bringen. Schränke niemals den Workflow der Anwendung künstlich ein. Das bedeutet aber auch, das man einen gewissen Mehraufwand treiben muss, wenn man Daten umstrukturieren will. Wenn beim speichern der Daten die ursprüngliche Formatierung erforderlich ist, muss man die Daten wieder zurückstrukturieren oder dieses bereits bei der Umstrukturierung berücksichtigen. Ok, kling ein wenig abstrakt.
    Ein Beispiel:

    Du hast eine DataTable, die du von einem Server bekommst. Dieses Daten willst du bearbeiten. Da du nicht alle Daten bearbeiten willst, möchtest du die Daten umstrukturieren. Die Dimensionen sind fest, und du entscheidest dich für ein Mehrdimensionales Array. Wenn du die Änderungen vorgenommen hast, möchtest du die empfangene DataTable geändert an einer Serverfunktion mit den Namen "Save" zurückgeben.
    Wenn man nun in das mehrdimensionale Array nur die Daten ablegt, muss man nach dem ändern, die DataTable ändern. Das ist natürlich zeitraubend und ineffektiv. Deshalb würde man hier mit Referenzen arbeiten. Man erzeugt ein Objekt, welches die Referenzen verwaltet. Diese Objekte werden anstatt des Wertes in das Array abgelegt. Dann kann man einfach die angebundene DataTable zurück an die "save"- Methode des Server übergeben.
    Beispiel für das :

    class ArrayObject
    		{
    			private DataRow _row;
    			private string _column;
    			public double rowValue
    			{
    				get
    				{ 
    					return System.Convert.ToDouble(_row[_column]);
    				}
    				set
    				{
    					_row[_column]= value;
    				}
    			}
    			public ArrayObject(DataRow row, string column)
    			{
    				_row=row;
    				_column=column;
    			}
    		}
    

    berücksichtige also immer, wie die Daten weiterverarbeitet werden sollen.

    Fazit: Wenn du nicht alle Daten verändern musst, sondern nur ein Teil, ist die Datenhaltung dein Problem und nicht die for/foreach-Schleife.



  • Fazit: Wenn du nicht alle Daten verändern musst, sondern nur ein Teil, ist die Datenhaltung dein Problem und nicht die for/foreach-Schleife.

    Full Ack! Die Wahl der Datenstruktur hat zusammen mit der Wahl der zu verwendenden Algorithmen den allerallerallergrößten Einfluss, deshalb ist die Diskussion for/foreach nur wenig sinnvoll.
    So arm möchte ich niemals dran sein, dass ich auf diese Ebene was ändern muss um nochmal minimal Performance rauszuholen. Heute findet "Optimierung" anscheinend weitverbreitet über solche Kleinigkeiten und mehr Prozessorpower statt, anstatt über Mathematik und Algorithmen.
    That's the wrong way to go...

    btw. finde ich es gerade deshalb besonders schade, dass das Collection-Framework von .Net ziemlich mager ist. Es gibt zwar noch die PowerCollections aber selbst beides zusammen kommt definitiv nicht an das von Java ran.


Anmelden zum Antworten