Meine Frage ist sehr einfach. In C oder C ++:
Nehmen wir an, die for
-Schleife ist wie folgt,
Meine Frage ist, ob die Berechnung a+b
für jede for
-Schleife durchgeführt wird oder ob sie nur einmal am Anfang der Schleife berechnet wird?
Für meine Anforderungen ist der Wert a+b
konstant. Wenn a+b
berechnet wird und auf den Wert someArray[a+b]
jedes Mal in der Schleife zugegriffen wird, würde ich eine temporäre Variable für someArray[a+b]
verwenden, um eine bessere Leistung zu erzielen.
Sie können herausfinden, wenn Sie sich den generierten Code ansehen
%Vor%und
%Vor% Sehen Sie sich die Ausgabe file.s
an und vergleichen Sie die beiden Versionen. Wenn someArray[a+b]
für alle Schleifenzyklen auf einen konstanten Wert reduziert werden kann, wird dies normalerweise vom Optimierer durchgeführt und in eine temporäre Variable oder ein Register übernommen.
Es verhält sich als ob es jedes Mal berechnet wurde. Wenn der Compiler optimiert und in der Lage ist nachzuweisen, dass sich das Ergebnis nicht ändert, darf die Berechnung aus der Schleife entfernt werden. Andernfalls wird es jedes Mal neu berechnet.
Wenn Sie sicher sind, dass das Ergebnis konstant ist und die Geschwindigkeit wichtig ist, verwenden Sie eine Variable zum Zwischenspeichern.
Erstens geben die C- und C ++ - Standards nicht vor, wie eine Implementierung i<someArray[a+b]
auswerten soll, nur dass das Ergebnis so aussehen muss, als ob es bei jeder Iteration ausgeführt würde (sofern das Programm anderen Sprachanforderungen entspricht).
Zweitens wird jede C- und C ++ - Implementierung von mäßiger Qualität das Ziel haben, die wiederholte Auswertung von Ausdrücken zu vermeiden, deren Wert sich nicht ändert, sofern die Optimierung nicht deaktiviert ist.
Drittens können verschiedene Dinge dieses Ziel beeinträchtigen, einschließlich:
a
, b
oder someArray
mit außerhalb der Funktion sichtbarem Bereich deklariert sind (z. B. im Dateibereich deklariert sind) und der Code in der Schleife andere Funktionen aufruft, kann dies die C- oder C ++ - Implementierung sein kann nicht feststellen, ob a
, b
oder someArray
während der Schleife geändert wurden. a
, b
oder someArray
oder ihre Elemente verwendet wird, kann die C- oder C ++ - Implementierung möglicherweise nicht feststellen, ob diese Adresse zum Ändern dieser Objekte verwendet wird. Dies schließt die Möglichkeit ein, dass someArray
ein in die Funktion übergebenes Array ist, so dass seine Adresse anderen Entitäten außerhalb der Funktion bekannt ist. a
, b
oder die Elemente von someArray
volatile
sind, muss die C- oder C ++ - Implementierung davon ausgehen, dass sie jederzeit geändert werden können. Betrachten Sie diesen Code:
%Vor% In diesem Code kann die C- oder C ++ - Implementierung normalerweise nicht wissen, ob otherArray
auf dasselbe Array (oder einen überlappenden Teil) wie someArray
zeigt. Daher muss angenommen werden, dass otherArray[i] = something;
someArray[a+b]
ändern kann.
Beachten Sie, dass ich bezüglich des größeren Ausdrucks someArray[a+b]
geantwortet habe und nicht nur den Teil, nach dem Sie gefragt haben, a+b
. Wenn Sie nur an a+b
interessiert sind, sind natürlich nur die Faktoren relevant, die a
und b
betreffen.
Hängt davon ab, wie gut der Compiler ist, welche Optimierungsstufen Sie verwenden und wie a
und b
deklariert sind.
Zum Beispiel, wenn a
und / oder b
volatile
Qualifier hat, dann muss der Compiler es jedes Mal lesen. In diesem Fall kann der Compiler keine Optimierung mit dem Wert von a+b
auswählen. Andernfalls sehen Sie sich den vom Compiler erzeugten Code an, um zu verstehen, was Ihr Compiler macht.
Es gibt kein Standardverhalten, wie dies in C nicht C ++ berechnet wird.
Ich wette, wenn a
und b
sich nicht über die Schleife ändern, wird es optimiert. Wenn someArray[a+b]
nicht berührt wird, wird es außerdem optimiert. Dies ist tatsächlich wichtiger, da fetching Operationen ziemlich teuer sind.
Das ist mit jedem halbwegs ordentlichen Compiler mit den meisten grundlegenden Optimierungen. Ich werde auch so weit gehen zu sagen, dass Leute, die sagen, dass sie immer bewerten, einfach falsch sind. Es ist nicht immer sicher, und es wird höchstwahrscheinlich wann immer möglich optimiert.
Die Berechnung wird pro for
loop durchgeführt. Obwohl der Optimierer schlau sein und ihn optimieren kann, wäre es besser, wenn Sie so etwas tun würden:
Es könnte jedes Mal berechnet werden oder es könnte optimiert werden. Dies hängt davon ab, ob a
und b
in einem Bereich existieren, in dem der Compiler garantieren kann, dass keine externe Funktion ihre Werte ändern kann. Das heißt, wenn sie sich in einem globalen Kontext befinden, kann der Compiler nicht garantieren, dass eine Funktion, die Sie in der Schleife aufrufen, sie ändert (es sei denn, Sie rufen keine Funktionen auf). Wenn sie sich nur im lokalen Kontext befinden, kann der Compiler versuchen, diese Berechnung zu optimieren.
Das Generieren von optimiertem und nicht optimiertem Assemblercode ist der einfachste Weg, dies zu überprüfen. Am besten ist es jedoch nicht, weil die Kosten für diese Summe so unglaublich günstig sind. Moderne Prozessoren sind sehr, sehr schnell und die Sache, die langsam ist, zieht Daten aus dem RAM in den Cache. Wenn Sie Ihren Code optimieren möchten, profilieren Sie ihn. rate nicht.
Die Berechnung a+b
wird bei jeder Iteration der Schleife ausgeführt, und dann wird das Nachschlagen in someArray
bei jeder Iteration der Schleife ausgeführt, so dass Sie möglicherweise viel Prozessorzeit durch eine temporäre Variable sparen können Set außerhalb der Schleife, zum Beispiel (wenn das Array ist ein Array von ints
sagen):
Sehr vereinfachte Erklärung:
Wenn der Wert an der Array-Position a+b
beispielsweise nur 5 wäre, wären das 5 Berechnungen und 5 Nachschlagevorgänge, also 10 Operationen, die durch eine Variable außerhalb der Schleife durch 8 ersetzt würden (5 Zugriffe (1 pro Iteration der Schleife), 1 Berechnung von a+b
, 1 Nachschlagen und 1 Zuweisung an die neue Variable) keine so große Einsparung. Wenn Sie jedoch mit größeren Werten arbeiten, zum Beispiel dem Wert im Array bei a+b
id 100, würden Sie möglicherweise 100 Berechnungen und 100 Abfragen durchführen, im Gegensatz zu 103 Operationen, wenn Sie eine Variable außerhalb der Schleife haben (100 Zugriffe ( 1 pro Iteration der Schleife, 1 Berechnung von a+b
, 1 Nachschlagen und 1 Zuweisung an die neue Variable).
Der Großteil der obigen Angaben hängt jedoch vom Compiler ab: Je nachdem, welche Switches Sie verwenden, welche Optimierungen der Compiler automatisch anwenden kann usw., kann der Code optimiert werden, ohne dass Sie Änderungen an Ihrem Code vornehmen müssen. Am besten ist es, die Vor- und Nachteile jedes Ansatzes speziell für Ihre aktuelle Implementierung abzuwägen, da das, was für eine große Anzahl von Iterationen geeignet sein mag, für eine kleine Anzahl nicht sehr effizient ist, oder Speicher möglicherweise ein Problem ist, das ein diktieren könnte unterschiedlicher Stil zu Ihrem Programm. . . Nun, Sie bekommen die Idee:)
Lassen Sie mich wissen, wenn Sie weitere Informationen benötigen:)
für den folgenden Code:
%Vor%Sie erhalten die folgende Assembly:
%Vor%Wenn Sie die Compiler-Optimierung anwenden, erhalten Sie die folgende Assembly:
%Vor%Grundsätzlich, in diesem Fall, mit meinem Compiler (MinGW 4.8.0), wird die Schleife "die Berechnung" unabhängig davon, ob Sie die bedingten Variablen innerhalb der Schleife ändern oder nicht (haben keine Assembly dafür gebucht , aber nimm mein Wort dafür, oder noch besser, tu es nicht und zerlege den Code selbst).
Wenn Sie die Optimierung anwenden, macht der Compiler ein wenig Magie und erstellt eine Reihe von Befehlen, die völlig unkenntlich sind.
Wenn Sie nicht möchten, dass Ihre Schleife durch eine Compiler-Aktion (-On) optimiert wird, deklariert eine Variable, der a+b
zugewiesen wird, Ihre Assembly um eine oder zwei Anweisungen.
Assembly:
%Vor%Denken Sie daran, der Assembler-Code, den ich hier gepostet habe, ist nur das relevante Snippet, es gibt ein bisschen mehr, aber es ist nicht relevant, soweit die Frage geht