Da Ihre zweite Frage konkreter ist, werde ich sie zuerst beantworten und dann Ihre erste Frage in dem durch die zweite Frage gegebenen Kontext aufgreifen. Ich wollte eine evidenzbasierte Antwort geben, die über das hinausgeht, was hier bereits steht.
Frage #2 : Erkennen die meisten Compiler, dass die Variable bereits deklariert wurde deklariert wurde und überspringen diesen Teil einfach, oder wird jedes Mal ein jedes Mal einen Platz im Speicher an?
Sie können diese Frage selbst beantworten, indem Sie Ihren Compiler stoppen, bevor der Assembler ausgeführt wird, und sich die asm ansehen (verwenden Sie die -S
Flagge, wenn Ihr Compiler eine gcc-ähnliche Schnittstelle hat, und -masm=intel
wenn Sie den hier verwendeten Syntax-Stil wünschen).
In jedem Fall laden moderne Compiler (gcc 10.2, clang 11.0) für x86-64 die Variable nur bei jedem Schleifendurchlauf neu, wenn Sie die Optimierungen deaktivieren. Betrachten Sie das folgende C++-Programm - für eine intuitive Zuordnung zu asm halte ich die Dinge größtenteils im C-Stil und verwende einen Integer anstelle eines Strings, obwohl die gleichen Prinzipien im String-Fall gelten:
#include <iostream>
static constexpr std::size_t LEN = 10;
void fill_arr(int a[LEN])
{
/* *** */
for (std::size_t i = 0; i < LEN; ++i) {
const int t = 8;
a[i] = t;
}
/* *** */
}
int main(void)
{
int a[LEN];
fill_arr(a);
for (std::size_t i = 0; i < LEN; ++i) {
std::cout << a[i] << " ";
}
std::cout << "\n";
return 0;
}
Wir können dies mit einer Version vergleichen, die den folgenden Unterschied aufweist:
/* *** */
const int t = 8;
for (std::size_t i = 0; i < LEN; ++i) {
a[i] = t;
}
/* *** */
Wenn die Optimierung deaktiviert ist, legt gcc 10.2 bei jedem Schleifendurchlauf der Deklaration-in-der-Schleife-Version 8 auf den Stack:
mov QWORD PTR -8[rbp], 0
.L3:
cmp QWORD PTR -8[rbp], 9
ja .L4
mov DWORD PTR -12[rbp], 8 ;
während dies bei der Out-of-Loop-Version nur einmal geschieht:
mov DWORD PTR -12[rbp], 8 ;
mov QWORD PTR -8[rbp], 0
.L3:
cmp QWORD PTR -8[rbp], 9
ja .L4
Wirkt sich das auf die Leistung aus? Ich habe keinen nennenswerten Unterschied in der Laufzeit zwischen ihnen mit meiner CPU (Intel i7-7700K) gesehen, bis ich die Anzahl der Iterationen in die Milliarden getrieben habe, und selbst dann war der durchschnittliche Unterschied weniger als 0,01s. Es handelt sich schließlich nur um eine einzige zusätzliche Operation in der Schleife. (Bei einer Zeichenkette ist der Unterschied bei den Operationen in der Schleife natürlich etwas größer, aber nicht so dramatisch).
Darüber hinaus ist die Frage weitgehend akademisch, denn bei einem Optimierungsgrad von -O1
oder höher gibt gcc identische asm für beide Quelldateien aus, ebenso wie clang. Zumindest in einfachen Fällen wie diesem ist es also unwahrscheinlich, dass es sich auf die Leistung auswirkt, egal wie. Natürlich sollte man bei einem realen Programm immer ein Profil erstellen, anstatt Annahmen zu treffen.
Frage 1 : Ist die Deklaration einer Variablen innerhalb einer Schleife eine gute oder schlechte Praxis?
Wie bei praktisch jeder Frage dieser Art kommt es darauf an. Wenn sich die Deklaration innerhalb einer sehr engen Schleife befindet und Sie ohne Optimierungen kompilieren, z. B. zu Debugging-Zwecken, ist es theoretisch möglich, dass das Verschieben der Deklaration außerhalb der Schleife die Leistung so weit verbessert, dass es bei Ihren Debugging-Bemühungen nützlich ist. Wenn dem so ist, könnte es sinnvoll sein, zumindest während der Fehlersuche. Und obwohl ich nicht glaube, dass es in einem optimierten Build einen Unterschied machen wird, können Sie/Ihr Paar/Ihr Team, wenn Sie einen beobachten, eine Entscheidung treffen, ob es das wert ist.
Gleichzeitig müssen Sie nicht nur berücksichtigen, wie der Compiler Ihren Code liest, sondern auch, wie er auf Menschen wirkt, Sie selbst eingeschlossen. Ich denke, Sie werden mir zustimmen, dass eine Variable, die im kleinstmöglichen Bereich deklariert ist, leichter zu überblicken ist. Wenn sie außerhalb der Schleife liegt, impliziert das, dass sie außerhalb der Schleife benötigt wird, was verwirrend ist, wenn das nicht der Fall ist. In einer großen Codebasis summieren sich kleine Verwirrungen wie diese mit der Zeit und können nach stundenlanger Arbeit ermüdend sein und zu dummen Fehlern führen. Das kann je nach Anwendungsfall viel kostspieliger sein als der Gewinn aus einer leichten Leistungsverbesserung.