| Code Kitchen Allgemeines Coder-Forum rund um das Programmieren eigenständiger, ausführbarer Programme. |
Diskussion: Doppelte for schleife: performance optimierung im Forum Code Kitchen, in der Kategorie Software Home; Anzeige Hi! Das problem einer doppelten for Schleife liegt daran, dass sie sehr langsam ist. Es ist jedoch leicht möglich ...
![]() |
| | #1 (permalink) |
| Registriert seit: 05.02.06 ![]() Likes: 0 | Anzeige Hi! Das problem einer doppelten for Schleife liegt daran, dass sie sehr langsam ist. Es ist jedoch leicht möglich eine doppelte in eine einfache umzuwandeln. Einige meinen vl, dass es keinen großen unterschied macht, aber der Unterschied ist ENORM. Ich hab diese variante in den Sprachen C, C++(native), c#, java und pawn getestet. Das Ergebnis war überall gleich, die einfache ist schneller :P Beispiel: Ich hab vor ca. nem jahr eine ShaderApi geschrieben und hab um jedes Pixel zu benutzen eine doppelte for-schleife gehabt. Code: for( int y = 0; x < max_y; y++ )
{
for( int x = 0; y < max_x; x++ )
{
//PAAR OPERATIONEN
}
} Bei dem Sobel - Operator hat diese Schreibweise bei einem Bild( 1024X768 ) ca 6-7 sekunden gedauert Hier die Umwandlung in eine einfache for-schleife Code: int size = max_x * max_y;
for( int i = 0; i < size; i++ )
{
int x = i % max_x;
int y = i / max_x;
//PAAR OPERATIONEN
} Zu beachten ist die Schreibweise der Berechnungen. x wäre in beiden Fällen die innere Schleife und y die Äußere. Hier ist jetzt der genaue Code was ihr wie ersetzen müsst: Code:
for( int var_aussen; var_aussen < aussen_max; var_aussen++ )
{
for( int var_innen; var_innen < innen_max; var_innen++ )
{
//...
}
}
//die gesammtgröße in eine variable speichern, da sie sonst jedes mal neu ausgerechnet werden würde.
int size = aussen_max * innen_max;
for( int i = 0; i < size; i++ )
{
int var_innen = i % innen_max;
int var_aussen = i / innen_max;
//...
} Also ihr seht schön eine einfache for-schleife benutzen :P Wenn ich mein altes Programm gefunden hab, uploade ich es mal. -- Greenberet |
| | |
| | #2 (permalink) |
| Senior Member Registriert seit: 28.08.05 ![]() Likes: 0 | wenn ich das jetzt richtig verstanden habe, bezieht sich das nur auf render-engines? denn wenn die innere schleife inkrementel und die äußere dekrementel zälht, dann sollte doch eigentlich keine optimierung möglich sein. nur um auf nummer sicher zu gehen... weiß eigentlich jemand, wie die performance mit zwei while-schleifen aussieht? bin grad dabei meinen laptop neu aufzusetzen... |
| | |
| HaBOT | - Anzeige - |
| |
| | #3 (permalink) |
| Themenstarter Registriert seit: 05.02.06 ![]() Likes: 0 | also das ganze geht nur wenn beide dekrement oder inrement sind wie die berechnung bei unterschiedlichen ist muss ich mir erst anschauen XD Ich glaube das ganze müsste man dann mit einer wertinvertierung gehen. und nein das ist nicht nur bei rendering, ich hab rendering nur als beispiel genommen weil das viel zeit in anspruch nimmt und man dort die zeit aber braucht. Bei anderen dingen ist die einfache auch schneller^^ mit while schleifen sieht das ganze im großen und ganzen gleich aus wie mit den for schleifen. |
| | |
| | #4 (permalink) | ||
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() ![]() Likes: 202 | Ich denke das hängt SEHR vom compiler ab Zitat:
Code: for(int i=0;i<MAX;i++)
{
ergebnis=i*20;
} Was Du vergleichst, ist ohne den "weitere Codeeinsicht" nicht wirklich vergleichsam: denn normalerweise ist eine Division zumindest auf einem Pentium/AMD immer noch langsamer als ein Vergleich - und davon hast Du gleich zwei.Ein "vernünftiger" Compiler würde es hier bei einer belassen (denn bei einer Division wird gleichzeitig auch der Rest festgehalten - so braucht man eigentlich nur eine, eventuell also die Reihenfolge % und / vertauschen - um dem Compiler bei der optimierung zu helfen) . Was ich vermute, warum bei Dir soviel mehr leistung rausgekommen ist: bessere Cache"hits", auch wenn solche Sachen auf einem P4 schwer vorherzusagen sind. Jedenfalls denke ich dass besonders bei Speicherzugriffen diese Zählvariablen alleine keine allzugroße Rolle spielen Ok, ein wenig von meiner Seite: Code: align 4
timer_begin 1000, REALTIME_PRIORITY_CLASS ;die schleife wird insgesamt 1000 000 000 durchlaufen
mov ebx,0 ;mov 0 ist schneller als XOR -> inteldoku +eigene tests
LOOP1:
mov ecx,0
LOOP2:
add ecx,1
cmp ecx,1000
jbe LOOP2
add ebx,1
cmp ebx,1000
jbe LOOP1
timer_end
print ustr$(eax)
print chr$(" ms, 'primitiver Loop'",13,10)
align 4
timer_begin 1000, REALTIME_PRIORITY_CLASS
mov ebx,1000
LOOP3:
mov ecx,1000
LOOP4:
sub ecx,1 ;auf Pentium4 schneller als dec ecx
jns LOOP4
sub ebx,1
jns LOOP3
timer_end
print ustr$(eax)
print chr$(" ms, 'verbesserter Loop'",13,10)
align 4
timer_begin 1000, REALTIME_PRIORITY_CLASS
mov edi,1000; unsere MAX Konstante, Optimierung die ein "älterer" Compiler nicht vornimmt!
mov ebx,0
LOOP5:
mov eax,ebx
mov edx,0
div edi
;jetzt ist in edx X wert und in EAX Y
add ebx,1 ;auf Pentium4 schneller als dec ecx
cmp ebx,1000000
jbe LOOP5
timer_end
print ustr$(eax)
print chr$(" ms, nur 'eine' schleife, leicht optmiert",13,10)
align 4
timer_begin 1000, REALTIME_PRIORITY_CLASS
mov edi,1000; unsere MAX Konstante, Optimierung die ein "älterer" Compiler nicht vornimmt!
mov ebx,1000000
LOOP6:
mov eax,ebx
mov edx,0
div edi
;jetzt ist in edx X wert und in EAX Y
sub ebx,1 ;auf Pentium4 schneller als dec ecx
jns LOOP6
timer_end
print ustr$(eax)
print chr$(" ms, nur 'eine' schleife, etwas besser optimiert",13,10)
mov eax, input(13,10,"Press enter to exit...")
exit
; ??????????????????????????????????????????????????????????????
end start Zitat:
und zwar wird jede Schleife 1000 000 000 mal asugeführt (die forschleifen jeweils 1000 mal und die funktion selber auch 1000 mal, damit man halbwegs was messen kann). alle schleifen sind gleich - sie laufen immer die gleiche Anzahl an Loops durch. Die ersten beiden sind jeweils: eine "naive" Schleife, dann eine "optimierete" (zählt rückwärts und erspart sich damit einen Vergleich) die anderen beiden sind jeweils zu einer zusammengefasst worden (nach Deiner Methode - also auch das Modulo/Teilen um X,Y Werte herauszufinden), wobei ich die erste nur "ein wenig" optimiert habe: sprich sie kommt nur mit einer Division aus (das wird ein "älterer" Compiler nicht berücksichtigen und fast mit sicherheit zwei daraus machen) und auch die Konstante lasse ich im Register (wiederum: bei älteren C/Delphi compilern kann man davon ausgehen, dass sie sie Lokal abspeichern und gleich wieder laden )die zweite Schleife ist wieder "rückwärts" und erspart sich dadurch einen Vergleich. Das ganze ist auf einen P4 ausgelegt (deswegen auch mov ecx,0 und nicht XOR - denn mov ist schneller, genauso ADD ECX,1 und nicht INC ECX). Meine Zahlen sind (P4, 3.0 GHz, HT): Durchlauf 1: 525 ms, 'primitiver Loop' 418 ms, 'verbesserter Loop' 9586 ms, nur 'eine' schleife, leicht optmiert 9210 ms, nur 'eine' schleife, etwas besser optimiert Durchlauf2: 526 ms, 'primitiver Loop' 446 ms, 'verbesserter Loop' 9613 ms, nur 'eine' schleife, leicht optmiert 9435 ms, nur 'eine' schleife, etwas besser optimiert Durchlauf3: 528 ms, 'primitiver Loop' 440 ms, 'verbesserter Loop' 9586 ms, nur 'eine' schleife, leicht optmiert 9110 ms, nur 'eine' schleife, etwas besser optimiert Wie man sieht, bringt die jeweilige "Minioptimierung" (sprich das rückwärtslaufenlassen) bei den "normalen" Schleifen am meisten. Die jeweiligen unteren Werte sind übrigens kein Fehler (wie auch ich anfangs dachte) wenn man die Division weglässt, sind die Werte z.T sogar besser als bei den oberen Schleifen (da weniger Instruktionen). Und wenn man bedenkt, dass ein "älterer" Compiler hier gar zwei Divisionen und ein paar unnötige Speicherzugriffe hätte - damit untermauere ich meine These, dass bei Dir andere Gründe für den Leistungszuwachs gesorgt haben Im Anhang ist eine Test.exe samt Quelltext und Includes. Damit kann jeder, der MASM32 v8 hat, damit rumspielen.
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. | ||
| | |
| | #5 (permalink) |
| Senior Member Registriert seit: 28.08.05 ![]() Likes: 0 | ![]() member of honour bzw mod ist das mindeste, was cdw werden kann: sein altruismus in ehren auf ewiglich (oder so) *thumbsup* nun aber wirklich gute nacht |
| | |
| | #6 (permalink) |
| Member of Honour ![]() Registriert seit: 03.10.01 ![]() Likes: 1 | @CDW: wenns aber nur um die division geht, die braucht man natürlich nicht. und das eine einzelne schleife weniger overhead produziert sollte eigtl. klar sein. |
| | |
| | #7 (permalink) | |||||||
| Themenstarter Registriert seit: 05.02.06 ![]() Likes: 0 | Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
[quote]Original von blueflash @CDW: wenns aber nur um die division geht, die braucht man natürlich nicht. und das eine einzelne schleife weniger overhead produziert sollte eigtl. klar sein.[/quote da geb ich dir vollkommen Recht. | |||||||
| | |
| | #8 (permalink) | ||||
| Moderator ![]() Registriert seit: 20.07.05 ![]() ![]() ![]() ![]() ![]() ![]() Likes: 202 | Zitat:
Zitat:
Zitat:
Und ein Forschleife sollte doch auch in etwa so übersetzt werden - zumindest Schablonenmäßig als Label mit einem Jump. Ob aber mit oder ohne ausgiebige Registernutzung - Div bleibt Div. Zitat:
ein einfaches Beispiel. Code 1: Code: for (int y=0;y<BILD_Y;y++)
for(int x=0;x<BIDL_X;x++)
{
ReadPixel(x,y);
} Code: for (int x=0;x<BILD_X;x++)
for(int y=0;y<BIDL_Y;y++)
{
ReadPixel(x,y,blue);
} viel schneller laufen als Code2 - denn die Daten liegen linear im Speicher pixel1,pixel2, ... , pixel2000 und werden auch in Blöcken beim Zugriff "gecacht". Der erste Code geht sequentiell durch, der zweite dagegen "hüpft" durch den Speicherraum durch (zugriff: y*Y_MAX+x) und macht dadurch Caching zurnichte - so dass man als Folge langsame Speicherzugriffe hat und insgesamt eine sehr schlechtere Perfomance. Und vielleicht hattest Du "Glück" mit der Umstellung und hast dadurch z.B die "magische" Cachegrenzengröße erreicht - so dass der Code komplett gecacht werden konnte. Die Wege des Compilers sind unergründlich Wäre aber interessant, wenn jemand das ganze mit einem "hochoptimierenden" Compler testet (z.B dem von Intel).
__________________ Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf! Selig, wer nichts zu sagen hat und trotzdem schweigt. | ||||
| | |
| | #9 (permalink) | |
| Themenstarter Registriert seit: 05.02.06 ![]() Likes: 0 | einigen wir uns darauf, dass es teilweise schneller und teilweise langsamer ist( osnt artet das hier noch aus )^^ Zitat:
y*X_MAX+x deswegen ist die formel ja auch Code: int x = i % X_MAX; int y = i / X_MAX; | |
| | |
| | #10 (permalink) |
| Senior Member Registriert seit: 02.10.01 ![]() Likes: 1 | /Neunmalkluger-Modus on Das was hier diskutiert wird ist unter dem Begriff "Big O Notation" bekannt. Eine einzelne Schleife hat die Komplexität von O(N), eine verschachtelte von O(N?). /Neunmalkluger-Modus off |
| | |
| | #11 (permalink) |
| Member of Honour ![]() Registriert seit: 03.10.01 ![]() Likes: 1 | /Neunmalklügerer Modus an Grundsätzlich richtig. Aber wenn man aus zwei verschachtelten schleifen die jeweils eine Komplexität von O(n) haben eine macht die die gleiche Funktionalität bietet, hat man i.d.R. eine neue Schranke m = n * n also ändert sich an der Komplexität gar nüscht. /Neunmalklügerer Modus aus |
| | |
| | #12 (permalink) |
| Senior Member Registriert seit: 02.10.01 ![]() Likes: 1 | Da in der Äusseren Schleife nun "drinnen" keine weitere Schleife mehr abläuft, verringert dies die Komplexität von O(N?) zu O(N log N) wenn ich mich noch recht daran erinnere. Auf jeden Fall ist die Komplexität kleiner geworden (auch wenns nur ein bischen ist). So genug über Big-O gelabert |
| | |
| | #13 (permalink) | |
| Registriert seit: 26.05.05 ![]() Likes: 0 | Hi! Ich habe mir das Ganze mal angeschaut und stelle fest: Es funktioniert nicht. Das Problem ist, dass wir in der Mathematik mit einem Dezimalsystem rechnen, wie der Code hier ja auch. Code: int size = max_x * max_y;
for( int i = 0; i < size; i++ )
{
int x = i % max_x;
int y = i / max_x;
//PAAR OPERATIONEN
} Zitat:
---------------------------------------------------------------- int size = max_x * max_y + max_x + max_y; for( int i = 0; i < size; i++ ) { int x = i % (max_x+1); int y = i / (max_x+1); //PAAR OPERATIONEN } --------------------------------------------------------------- Ich hoffe, ich habe den C++-Code richtig geschrieben, kann kaum C++. cu gabriel | |
| | |
| | #14 (permalink) |
| Themenstarter Registriert seit: 05.02.06 ![]() Likes: 0 | Mein Code stimmt schon. deiner ist falsch. du vergisst dass man mit 0 zu zählen beginnt und nicht mit 1 sprich max_x = 5 mögliche pos. für x: 0, 1, 2, 3, 4 |
| | |
| | #15 (permalink) |
| Registriert seit: 26.05.05 ![]() Likes: 0 | Hi! Kann sein, dass ich das falsch verstanden habe. Wenn du also sagst Maximum=5, dann willst du 5 gar nicht erreichen, sondern nur bis 4 gehen? Da macht zwar die Bezeichnung MAX keinen Sinn, aber dann stimmt dein Code natürlich, aber wenn du wirklich bis einschließlich zum Maximum gehen willst, wirst du meinen nehmen müssen. cu Gabriel PS: Jetzt sehe ich es gerade: Du hast als Bedingung gestellt: x < max_x. Sorry, habe ich wegen der verwirrenden Bezeichnung "max_x" übersehen. |
| | |
![]() |
| - Anzeige - | |
| |
| Themen-Optionen | |
| Ansicht | |
| |
Ähnliche Themen | ||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Doppelte IP im Netzwerk | sw33tlull4by | Network · LAN, WAN, Firewalls | 3 | 10.07.09 19:16 |
| hilfe bei query optimierung | easteregg | (Web-) Design und webbasierte Sprachen | 2 | 08.07.09 20:15 |
| GalleryScript - Performance Optimierung | reaLInsanity | (Web-) Design und webbasierte Sprachen | 9 | 22.07.08 18:07 |
| 640x480 Optimierung | webfreak | (Web-) Design und webbasierte Sprachen | 6 | 06.02.07 21:31 |
| Kernel-Optimierung | Nesssus | Linux/UNIX | 3 | 24.02.06 13:38 |