Compiler zur Anweisungsoptimierung
Unter welchen Bedingungen optimiert der Compiler For-Anweisungen?
Die in diesem Blog verfassten Artikel wurden mit dem Ziel erstellt, zu unterrichten. Außerdem können die Informationen im Artikel falsch sein, also seien Sie bitte vorsichtig.
Aus Sicht der CPU lautet die in der For-Anweisung verwendete JMP-Anweisung: Da es zu Leistungseinbußen kommt, wenn die Pipeline unterbrochen ist oder die Verzweigungsvorhersage fehlschlägt, Der Compiler versucht, JMP-Anweisungen nicht zu verwenden, wenn die Bedingungen erfüllt sind.
- Wenn die Anzahl der Iterationen zur Kompilierungszeit als Konstante bestätigt wird.
Optimierbar: for (int i = 0; i < 4; i++) (in dem Wissen, dass es viermal wiederholt wird) Optimierungsschwierigkeit: for (int i = 0; i < n; i++) (wenn n eine zur Laufzeit bestimmte Variable ist)
//Originalcode
for (int i = 0; i < 3; i++) {
arr[i] = 0;
}
//Nach der Compileroptimierung
arr[0] = 0;
arr[1] = 0;
arr[2] = 0;
Assemblersprache vor der Optimierung
.L2:
cmp DWORD PTR [rbp-4], 2 ;Vergleichen Sie, wenn i < 3
jg .L3 ;Sind die Bedingungen nicht erfüllt, endet die Schleife (Sprung zu L3).
mov eax, DWORD PTR [rbp-4]
cdqe
mov DWORD PTR [rbp-32+rax*4], 0 ;arr[i] = 0 Zuweisung
add DWORD PTR [rbp-4], 1 ; i++
jmp .L2 ;Unbedingter Rücksprung zum Schleifenstartpunkt (.L2) (JMP)
.L3:
Optimierte Assemblersprache
mov DWORD PTR [rdi], 0 ; arr[0] = 0
mov DWORD PTR [rdi+4], 0 ;arr[1] = 0 (+4, weil int 4 Bytes ist)
mov DWORD PTR [rdi+8], 0 ;arr[2] = 0 (+8, weil int 4 Bytes ist)
- Wenn das Endergebnis zur Kompilierungszeit im Voraus berechnet werden kann.
Wenn die Logik innerhalb der Schleife nicht von externen Ein- und Ausgängen abhängt, wird der Ergebniswert durch eine Konstante ersetzt.
//Originalcode
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
//Nach der Compileroptimierung
//JMP führt zur Laufzeit nicht einmal Additionsoperationen durch.
return 5050;
Assemblersprache vor der Optimierung
.L5:
cmp DWORD PTR [rbp-4], 100 ;Vergleichen Sie, wenn i <= 100
jg .L6 ;Oder beenden Sie die Schleife
mov eax, DWORD PTR [rbp-4]
add DWORD PTR [rbp-8], eax ; sum += i
add DWORD PTR [rbp-4], 1 ; i++
jmp .L5 ;Zurück zum Schleifenanfang springen (.L5) (JMP)
.L6:
mov eax, DWORD PTR [rbp-8] ;Bereiten Sie sich auf die Rückgabe von Ergebnissen vor
Assemblersprache nach der Optimierung
mov eax, 5050 ;Geben Sie den berechneten Ergebniswert 5050 in das EAX-Register ein.
ret ;sofort zurückkommen
- Wenn die Codelänge auch bei Schleifenerweiterung kurz ist.
Das Erweitern einer Schleife wie Code 1 wird als Schleifenerweiterung oder Schleifenabrollen bezeichnet. Wenn die Codelänge aufgrund der Schleifenerweiterung zu lang wird, ist dies eine Verschwendung des Befehls-Cache-Speichers der CPU. Wenn die Codegröße innerhalb der Schleife nicht klein ist, ist Loop Unrolling nicht optimiert.
- Wenn innerhalb der Schleife kein komplexer Kontrollfluss vorhanden ist.
Es gibt eine Pause-, Fortsetzungs-, Goto- oder komplexe if-else-Anweisung innerhalb der Schleife oder Wenn ein externer Funktionsaufruf eingebunden ist, erweitert der Compiler die Schleife nicht unachtsam.
Wenn es eine E/A-Funktion wie printf gibt, Da wir die auf dem Bildschirm angezeigten Nebenwirkungen schützen müssen, führen wir JMP ehrlich durch und rufen die Funktion auf, ohne die Schleife auszulassen.
Darüber hinaus muss die Compiler-Optimierungsoption aktiviert sein, um eine Optimierung wie oben durchzuführen.