Arokh
2006-10-12, 19:08:16
Hi Leute,
ich habe heute mal folgendes ausprobiert. Ich habe in Visual C++ 6 ein kleines Programm geschrieben, das einen einfachen Gauß-Algorithmus, d.h. die Addition aller ganzen Zahlen zwischen k1 und k2, ausführt, und mir dann im Debugger die Dissassemblierung anzeigen lassen, um mal ein Bild davon zu haben, was für Assemblercode VC++ 6 fabriziert. Der C++ Code sah so aus:
int i;
int sum = 0;
int imax = k2;
for (i = k1; i <= imax; i++)
{
sum += i;
}
k1 und k2 sind die beiden ganzen Zahlen, zwischen denen alle Zahlen addiert werden sollen, und die an anderer Stelle festgelegt werden.
Der Assembler-Code sieht jetzt so aus:
0040269E mov ecx,dword ptr [i]
004026A1 add ecx,1
004026A4 mov dword ptr [i],ecx
004026A7 mov edx,dword ptr [i]
004026AA cmp edx,dword ptr [imax]
004026AD jg CGaussDlg::ThreadCalc+0AAh (004026ba)
004026AF mov eax,dword ptr [sum]
004026B2 add eax,dword ptr [i]
004026B5 mov dword ptr [sum],eax
004026B8 jmp CGaussDlg::ThreadCalc+8Eh (0040269e)
So weit ich das verstehe, besagt dieser Code folgendes:
0040269E: schreibe Wert der Variablen in in Register ECX
004026A1: addiere 1 zum Wert in Register ECX
004026A4: schreibe Wert in Register ECX in Variable i zurück
004026A7: schreibe Wert der Variable i in Register EDX
004026AA: vergleiche Wert in Register EDX mit Wert der Variable imax
004026AD: <uninteressant während die Schleife läuft>
004026AF: schreibe den Wert der Variable sum in Register EAX
004026B2: addiere Wert der Variable i zum Wert i Register EAX
004026B5: schreibe Inhalt von EAX in Variable sum
004026B8: kehre zum Schleifeneingang zurück
Aber ist dieser Code nicht furchtbar ineffizient?
Schauen wir uns z.B. Variable i an: i wird zuerst aus dem Speicher in Register ECX geladen, dort um 1 inkrementiert, in den Speicherzurückgeschrieben, wiederum aus dem Speicher in Register EDX geladen und dort endlich mit imax verglichen.
Innerhalb der Schleife ein ähnlicher Aufwand mit Variable sum: aus dem Speicher in Register EAX einlesen, addiert, wieder in den Speicher geschrieben, um beim nächsten Schleifendurchlauf von neuem in EAX geladen zu werden.
Wäre es nicht viel effizienter, dieses ganze Hin- und Hergeschiebe zwischen Speicher und CPU-Registern bleibenzulassen? Könnte man nicht i und sum feste Register (ECX und EAX) zuweisen und während der ganzen Rechenoperation darin belassen. Mir schwebt etwa folgender Assemblercode vor:
004026A1 add ecx,1
004026A4 mov edx,ecx
004026A7 cmp edx,dword ptr [imax]
004026AA jg CGaussDlg::ThreadCalc+0AAh (004026b3)
004026AD add eax,ecx
004026AF jmp CGaussDlg::ThreadCalc+8Eh (004026a1)
man müßte natürlich vorher sicherstellen, daß die Initialwerte von i und sum in den Registern stehen, und hinterher dafür sorgen, daß deren Finalwerte in den Speicher geschrieben werden. Das bräuchte man aber nur jeweils einmal machen, und nicht bei jedem Schleifendurchlauf.
Anmerkung: zum Vergleich mit imax habe ich i dann vorsichtshalber doch in ein zweites Register (EDX) kopiert, da ich annehme, daß die Vergleichsoperation den Registerinhalt überschreibt.
Testweise habe ich den Variablen i, sum und imax bei der Deklaration das Schlüsselwort register vorangestellt, am produzierten Assemblercode änderte sich dadurch aber nichts.
Hab ich in meiner Überlegung einen Denkfehler drin? Oder ist es vielleicht so, daß mir VC++ deswegen so ineffizienten Code erzeugt, weil ich im Debug-Modus bin und alle Optimierung dadurch abgeschaltet sind?
ich habe heute mal folgendes ausprobiert. Ich habe in Visual C++ 6 ein kleines Programm geschrieben, das einen einfachen Gauß-Algorithmus, d.h. die Addition aller ganzen Zahlen zwischen k1 und k2, ausführt, und mir dann im Debugger die Dissassemblierung anzeigen lassen, um mal ein Bild davon zu haben, was für Assemblercode VC++ 6 fabriziert. Der C++ Code sah so aus:
int i;
int sum = 0;
int imax = k2;
for (i = k1; i <= imax; i++)
{
sum += i;
}
k1 und k2 sind die beiden ganzen Zahlen, zwischen denen alle Zahlen addiert werden sollen, und die an anderer Stelle festgelegt werden.
Der Assembler-Code sieht jetzt so aus:
0040269E mov ecx,dword ptr [i]
004026A1 add ecx,1
004026A4 mov dword ptr [i],ecx
004026A7 mov edx,dword ptr [i]
004026AA cmp edx,dword ptr [imax]
004026AD jg CGaussDlg::ThreadCalc+0AAh (004026ba)
004026AF mov eax,dword ptr [sum]
004026B2 add eax,dword ptr [i]
004026B5 mov dword ptr [sum],eax
004026B8 jmp CGaussDlg::ThreadCalc+8Eh (0040269e)
So weit ich das verstehe, besagt dieser Code folgendes:
0040269E: schreibe Wert der Variablen in in Register ECX
004026A1: addiere 1 zum Wert in Register ECX
004026A4: schreibe Wert in Register ECX in Variable i zurück
004026A7: schreibe Wert der Variable i in Register EDX
004026AA: vergleiche Wert in Register EDX mit Wert der Variable imax
004026AD: <uninteressant während die Schleife läuft>
004026AF: schreibe den Wert der Variable sum in Register EAX
004026B2: addiere Wert der Variable i zum Wert i Register EAX
004026B5: schreibe Inhalt von EAX in Variable sum
004026B8: kehre zum Schleifeneingang zurück
Aber ist dieser Code nicht furchtbar ineffizient?
Schauen wir uns z.B. Variable i an: i wird zuerst aus dem Speicher in Register ECX geladen, dort um 1 inkrementiert, in den Speicherzurückgeschrieben, wiederum aus dem Speicher in Register EDX geladen und dort endlich mit imax verglichen.
Innerhalb der Schleife ein ähnlicher Aufwand mit Variable sum: aus dem Speicher in Register EAX einlesen, addiert, wieder in den Speicher geschrieben, um beim nächsten Schleifendurchlauf von neuem in EAX geladen zu werden.
Wäre es nicht viel effizienter, dieses ganze Hin- und Hergeschiebe zwischen Speicher und CPU-Registern bleibenzulassen? Könnte man nicht i und sum feste Register (ECX und EAX) zuweisen und während der ganzen Rechenoperation darin belassen. Mir schwebt etwa folgender Assemblercode vor:
004026A1 add ecx,1
004026A4 mov edx,ecx
004026A7 cmp edx,dword ptr [imax]
004026AA jg CGaussDlg::ThreadCalc+0AAh (004026b3)
004026AD add eax,ecx
004026AF jmp CGaussDlg::ThreadCalc+8Eh (004026a1)
man müßte natürlich vorher sicherstellen, daß die Initialwerte von i und sum in den Registern stehen, und hinterher dafür sorgen, daß deren Finalwerte in den Speicher geschrieben werden. Das bräuchte man aber nur jeweils einmal machen, und nicht bei jedem Schleifendurchlauf.
Anmerkung: zum Vergleich mit imax habe ich i dann vorsichtshalber doch in ein zweites Register (EDX) kopiert, da ich annehme, daß die Vergleichsoperation den Registerinhalt überschreibt.
Testweise habe ich den Variablen i, sum und imax bei der Deklaration das Schlüsselwort register vorangestellt, am produzierten Assemblercode änderte sich dadurch aber nichts.
Hab ich in meiner Überlegung einen Denkfehler drin? Oder ist es vielleicht so, daß mir VC++ deswegen so ineffizienten Code erzeugt, weil ich im Debug-Modus bin und alle Optimierung dadurch abgeschaltet sind?