Angenommen, ich habe den folgenden C-Code:
int i = 5;
int j = 10;
int result = i + j;
Wenn ich so oft eine Schleife durchführe, wäre die Verwendung dann schneller int result = 5 + 10
? Ich erstelle häufig temporäre Variablen, um meinen Code besser lesbar zu machen, beispielsweise wenn die beiden Variablen aus einem Array mit einem langen Ausdruck zur Berechnung der Indizes abgerufen wurden. Ist das in Bezug auf die Leistung in C schlecht? Was ist mit anderen Sprachen?
c
performance
temporary
Elliot Gorokhovsky
quelle
quelle
int result = 15 ;
Antworten:
Ein moderner Optimierungs-Compiler sollte diese Variablen wegoptimieren , zum Beispiel wenn wir das folgende Beispiel in Godbolt mit der
gcc
Verwendung der-std=c99 -O3
Flags verwenden ( sehen Sie es live ):#include <stdio.h> void func() { int i = 5; int j = 10; int result = i + j; printf( "%d\n", result ) ; }
Dies führt zu folgender Montage:
movl $15, %esi
Für die Berechnung von
i + j
ist dies eine Form der konstanten Ausbreitung .Beachten Sie, dass ich das hinzugefügt
printf
habe, damit wir einen Nebeneffekt haben, sonstfunc
wäre weg optimiert worden für:Diese Optimierungen sind nach der Als-ob-Regel zulässig, nach der der Compiler nur das beobachtbare Verhalten eines Programms emulieren muss. Dies wird im Entwurf des C99-Standardabschnitts
5.1.2.3
Programmausführung behandelt, in dem es heißt:Siehe auch: Optimieren von C ++ - Code: Konstantfalten
quelle
gcc
Gebrauchgcc -S -o asm_output.s app.c
Dies ist eine einfache Aufgabe für einen optimierenden Compiler. Es werden alle Variablen löschen und ersetzen
result
mit15
.Das ständige Falten in SSA-Form ist so ziemlich die grundlegendste Optimierung, die es gibt.
quelle
Das von Ihnen angegebene Beispiel ist für einen Compiler leicht zu optimieren. Die Verwendung lokaler Variablen zum Zwischenspeichern von Werten aus globalen Strukturen und Arrays kann die Ausführung Ihres Codes beschleunigen. Wenn Sie beispielsweise etwas aus einer komplexen Struktur in einer for-Schleife abrufen, in der der Compiler nicht optimieren kann und Sie wissen, dass sich der Wert nicht ändert, können die lokalen Variablen viel Zeit sparen.
Sie können GCC (auch andere Compiler) verwenden, um den Zwischenassemblycode zu generieren und zu sehen, was der Compiler tatsächlich tut.
Hier wird diskutiert, wie die Baugruppenlisten aktiviert werden: Verwenden Sie GCC, um eine lesbare Baugruppe zu erstellen?
Es kann lehrreich sein, den generierten Code zu untersuchen und festzustellen, was ein Compiler tatsächlich tut.
quelle
Während alle möglichen trivialen Unterschiede zum Code das Verhalten des Compilers auf eine Weise stören können, die die Leistung geringfügig verbessert oder verschlechtert, sollte es im Prinzip keinen Leistungsunterschied machen, ob Sie temporäre Variablen wie diese verwenden, solange die Bedeutung des Programms nicht stimmt geändert. Ein guter Compiler sollte in beiden Fällen denselben oder vergleichbaren Code generieren, es sei denn, Sie erstellen absichtlich mit deaktivierter Optimierung, um Maschinencode zu erhalten, der so nah wie möglich an der Quelle liegt (z. B. zu Debugging-Zwecken).
quelle
int a = arr[i]
der Compiler den Wert über Funktionsaufrufe und Schreibvorgänge über andere Zeiger in einem Register behalten.Sie haben das gleiche Problem wie ich, wenn ich versuche zu lernen, was ein Compiler tut - Sie erstellen ein triviales Programm, um das Problem zu demonstrieren, und untersuchen die Assembly-Ausgabe des Compilers, um festzustellen, dass der Compiler alles optimiert hat Sie haben versucht, es zu beseitigen. Sie können sogar eine ziemlich komplexe Operation in main () finden, die auf im Wesentlichen reduziert ist:
push "%i" push 42 call printf ret
Ihre ursprüngliche Frage lautet nicht "Was passiert mit
int i = 5; int j = 10...
?" aber "Verursachen temporäre Variablen im Allgemeinen eine Laufzeitstrafe?"Die Antwort ist wahrscheinlich nicht. Sie müssen sich jedoch die Assembly-Ausgabe für Ihren speziellen, nicht trivialen Code ansehen. Wenn Ihre CPU viele Register hat, wie z. B. ein ARM, befinden sich i und j sehr wahrscheinlich in Registern, genauso wie wenn diese Register den Rückgabewert einer Funktion direkt speichern würden. Zum Beispiel:
int i = func1(); int j = func2(); int result = i + j;
ist mit ziemlicher Sicherheit genau der gleiche Maschinencode wie:
int result = func1() + func2();
Ich schlage vor, dass Sie temporäre Variablen verwenden, wenn sie das Verstehen und Verwalten des Codes erleichtern, und wenn Sie wirklich versuchen, eine Schleife zu straffen, werden Sie trotzdem die Assembly-Ausgabe untersuchen, um herauszufinden, wie Sie so viel Leistung wie möglich verfeinern können möglich. Aber opfern Sie nicht die Lesbarkeit und Wartbarkeit für ein paar Nanosekunden, wenn dies nicht notwendig ist.
quelle
main()
mit Konstanten zur Kompilierungszeit. Beispiel: Hier ist ein einfaches Beispiel für die Summierung eines Float-Arrays mit gcc asm-Ausgabe von godbolt: goo.gl/RxIFEFi
und verwendetj
. Mein Beispiel wäre, Parameter zu erstelleni
und zuj
funktionieren , damit sie nur in Registern am Anfang des Codes für die Funktion sitzen.