Verlangsamen temporäre Variablen mein Programm?

Lesezeit: 8 Minuten

Benutzeravatar von Elliot Gorokhovsky
Elliot Gorokhovsky

Angenommen, ich habe den folgenden C-Code:

int i = 5;
int j = 10;
int result = i + j;

Wenn ich das viele Male durchlaufe, wäre es schneller zu verwenden int result = 5 + 10? Ich erstelle häufig temporäre Variablen, um meinen Code lesbarer zu machen, zum Beispiel, wenn die beiden Variablen aus einem Array mit einem langen Ausdruck zur Berechnung der Indizes abgerufen wurden. Ist das leistungsmäßig in C schlecht? Was ist mit anderen Sprachen?

  • Durch die Optimierung des Compilers wird dieser Code so geändert, dass er effektiv wird: int result = 15 ;

    – 2501

    15. November 2014 um 19:10 Uhr


  • Der Compiler optimiert Ihren Code. Es ist produktiver, sich auf Dinge wie (einen Teil) einer Berechnung zu konzentrieren, die innerhalb einer Schleife wiederholt wird, was besser vor Beginn der Schleife erledigt werden sollte.

    – Wetterfahne

    15. November 2014 um 19:10 Uhr

  • Ich denke, dass er irgendwelche temporären Variablen meint, dh: verwendet a = b + c; d = a + e; langsamer als mit a = b + c + d + e; Es kann möglicherweise mehr Speicher verbrauchen, wenn es auf eine Weise ausgeführt wird, die der Compiler nicht optimieren kann, aber es sollte nicht langsamer sein. beste Konzentration oder Arbeitsproduktivität, es sei denn, es handelt sich um einen kommerziellen und kritischen Leistungscode.

    – LebenindenBäumen

    15. November 2014 um 23:05 Uhr


  • @WeatherVane, obwohl die meisten Compiler dies zumindest bis zu einem gewissen Grad auch tun würden. Im Allgemeinen denke ich, dass es besser wäre, sich auf die Wartbarkeit des Codes zu konzentrieren, anstatt auf solche Mikrooptimierungen.

    – Glühwürmchen

    16. November 2014 um 2:42 Uhr

  • @PeteBecker Ich fürchte, das ist kein produktiver Vorschlag. Es ist ziemlich einfach, so etwas zu versuchen und einen falschen Eindruck zu bekommen, weil Sie zufällig einen Fall ausgewählt (oder nicht ausgewählt) haben, der eine Ausnahme von der allgemeinen Regel darstellt. Ohne ein klares Verständnis dafür, wie ein Compiler funktioniert, sollte Sie das Testen einiger Fälle in keiner Weise davon überzeugen, dass dies für alle Fälle gilt; Solche Verallgemeinerungen können sehr riskant sein und führen oft zu Fehlern.

    – Jules

    16. November 2014 um 19:18 Uhr

Benutzeravatar von Shafik Yaghmour
Shafik Yaghmur

Ein moderner optimierender Compiler sollte diese Variablen wegoptimieren, zum Beispiel wenn wir das folgende Beispiel in verwenden Gottriegel mit gcc Verwendung der -std=c99 -O3 Flaggen (live sehen):

#include <stdio.h>

void func()
{
  int i = 5;
  int j = 10;
  int result = i + j;

  printf( "%d\n", result ) ;
}

es wird in der folgenden Baugruppe resultieren:

movl    $15, %esi

für die Berechnung von i + jdas ist eine Form von ständige Ausbreitung.

Hinweis, ich habe die hinzugefügt printf damit wir sonst eine Nebenwirkung haben func wäre wegoptimiert worden zu:

func:
  rep ret

Diese Optimierungen sind unter der erlaubt Als-ob-Regel, die nur erfordert, dass der Compiler das beobachtbare Verhalten eines Programms emuliert. Dies wird im Entwurf des C99-Standardabschnitts behandelt 5.1.2.3 Programmausführung was sagt:

In der abstrakten Maschine werden alle Ausdrücke so ausgewertet, wie es die Semantik vorgibt. Eine tatsächliche Implementierung muss einen Teil eines Ausdrucks nicht auswerten, wenn daraus abgeleitet werden kann, dass sein Wert nicht verwendet wird und dass keine erforderlichen Nebeneffekte erzeugt werden (einschließlich solcher, die durch das Aufrufen einer Funktion oder den Zugriff auf ein flüchtiges Objekt verursacht werden).

Siehe auch: Optimierung des C++-Codes: Constant-Folding

  • Wie kann ich sehen, welche Assembly mein C-Code generiert?

    – Syntagma

    15. November 2014 um 19:11 Uhr

  • @REACHUS, unter gcc verwenden gcc -S -o asm_output.s app.c

    – David Ranieri

    15. November 2014 um 19:14 Uhr

  • Ich glaube nicht, dass dies die Frage beantwortet. Das gegebene Beispiel ist einfach, aber ich denke, der Teil über „wenn die beiden Variablen aus einem Array mit einem langen Ausdruck erhalten wurden, um die Indizes zu berechnen“ ist hier wichtiger, und das kann nicht wegoptimiert werden, oder doch?

    – Arturo Torres Sanchez

    16. November 2014 um 0:47 Uhr


  • @ArturoTorresSánchez Variablen sind nur ein menschliches Konzept. Es gibt keine wirklichen Variablen. Was der Compiler sieht, sind Verweise auf Ausdrücke und dann der Assemblercode, dem sie entsprechen. Wenn jemand nach dem Variablen-Overhead fragt, bedeutet dies wirklich den Speicher-Overhead der Ergebnis-Assembler-Anweisungen dieser Referenzen (z. B. Register- / Speicher-Lese- / Schreibvorgänge). Das “Löschen” einer Variablen bedeutet, dass der Compiler klug genug war zu erkennen, dass es keinen Grund gibt, diese “Referenz” erneut aus dem Speicher zu laden, da sich die Daten in einer anderen Referenz befinden, auf die sie in diesem Zusammenhang zugreifen kann … (1)

    – Manu343726

    16. November 2014 um 13:33 Uhr


  • @ArturoTorresSánchez … (in diesem Fall das Array-Element). Und heutzutage beherrschen Compiler globale Optimierungen und das Falten von Ausdrücken sehr gut (siehe die Antwort unten zum SSA-Formular).

    – Manu343726

    16. November 2014 um 13:34 Uhr


Dies ist eine einfache Aufgabe, die für einen optimierenden Compiler zu optimieren ist. Es werden alle Variablen gelöscht und ersetzt result mit 15.

Ständiges Einklappen SSA-Formular ist so ziemlich die grundlegendste Optimierung, die es gibt.

  • Ja, aber da “Ergebnis” technisch unbenutzt ist, wäre das Programm leer.

    – alexyorke

    16. November 2014 um 20:49 Uhr

  • Wenn dies das ganze Programm ist, dann wäre es tatsächlich leer. Wenn nicht, wird 15 in alle Verwendungen “eingefügt”.

    – usr

    16. November 2014 um 20:53 Uhr

Benutzeravatar von Steven Smith
Stefan Schmied

Das Beispiel, das Sie gegeben haben, ist für einen Compiler einfach zu optimieren. Die Verwendung lokaler Variablen zum Zwischenspeichern von Werten, die aus globalen Strukturen und Arrays gezogen werden, kann die Ausführung Ihres Codes tatsächlich beschleunigen. Wenn Sie beispielsweise etwas aus einer komplexen Struktur innerhalb einer for-Schleife abrufen, bei der der Compiler nicht optimieren kann, und Sie wissen, dass sich der Wert nicht ändert, können die lokalen Variablen einiges an Zeit sparen.

Sie können GCC (auch andere Compiler) verwenden, um den Zwischen-Assemblercode zu generieren und zu sehen, was der Compiler tatsächlich tut.

Hier wird diskutiert, wie die Assembler-Listen aktiviert werden: Verwenden von GCC zum Erstellen einer lesbaren Assemblierung?

Es kann aufschlussreich sein, den generierten Code zu untersuchen und zu sehen, was ein Compiler tatsächlich tut.

  • Dies ist die nützlichere Antwort. Die anderen gingen in den sich ständig faltenden Teil der Dinge ein und nicht in die lokale Kopie eines Speicherortspunkts. Das Zuweisen von Globals und Array-Elementen zu Lokalen ist oft hilfreich, wenn der Compiler nicht beweisen kann, dass Eingabe-Arrays sich nicht überlappen oder dass eine unbekannte Funktion, die aufgerufen wird, keine Änderungen am Array vornehmen kann. Dies hindert den Compiler häufig daran, dieselbe Speicherstelle mehrmals neu zu laden.

    – Peter Cordes

    5. August 2015 um 2:08 Uhr

Während alle möglichen trivialen Unterschiede zum Code das Verhalten des Compilers auf eine Weise stören können, die die Leistung leicht verbessert oder verschlechtert, sollte es im Prinzip keinen Unterschied in der Leistung machen, ob Sie solche temporären Variablen verwenden, solange die Bedeutung des Programms dies nicht ist geändert. Ein guter Compiler sollte in beiden Fällen denselben oder vergleichbaren Code generieren, es sei denn, Sie bauen absichtlich mit ausgeschalteter Optimierung, um Maschinencode zu erhalten, der so nah wie möglich an der Quelle liegt (z. B. für Debugging-Zwecke).

Scotts Benutzeravatar
Scott

Sie leiden unter dem gleichen 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, nur um festzustellen, dass der Compiler alles optimiert hat du hast versucht, es zu beseitigen. Möglicherweise finden Sie sogar eine ziemlich komplexe Operation in main(), die auf das Wesentliche reduziert ist:

push "%i"
push 42
call printf 
ret

Ihre ursprüngliche Frage ist nicht “was passiert mit int i = 5; int j = 10...?”, aber “verursachen temporäre Variablen im Allgemeinen eine Laufzeitstrafe?”

Die Antwort ist wahrscheinlich nicht. Aber Sie müssten sich die Assembly-Ausgabe für Ihren speziellen, nicht trivialen Code ansehen. Wenn Ihre CPU viele Register hat, wie ein ARM, dann befinden sich i und j sehr wahrscheinlich in Registern, genau so, als ob 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 derselbe Maschinencode wie:

int result = func1() + func2();

Ich schlage vor, dass Sie temporäre Variablen verwenden, wenn sie den Code leichter verständlich und wartungsfreundlicher machen, und wenn Sie wirklich versuchen, eine Schleife zu straffen, werden Sie sowieso in die Assembly-Ausgabe schauen, um herauszufinden, wie Sie so viel Leistung wie möglich optimieren können möglich. Aber opfern Sie die Lesbarkeit und Wartbarkeit nicht für ein paar Nanosekunden, wenn das nicht notwendig ist.

  • Die Lösung besteht darin, in der Compilerausgabe nach Funktionen zu suchen, die mit Parametern arbeiten, anstatt main() Betrieb mit Kompilierzeitkonstanten. Hier ist zB ein einfaches Beispiel für die Summierung eines Float-Arrays mit der Ausgabe von gcc asm von godbolt: goo.gl/RxIFEF

    – Peter Cordes

    10. November 2015 um 2:28 Uhr

  • Ich glaube, das habe ich gesagt: “Sie müssten sich die Assembly-Ausgabe für Ihren speziellen, nicht trivialen Code ansehen”. 😉

    – Scott

    10. November 2015 um 22:08 Uhr

  • Ich habe versucht zu sagen, dass Sie ziemlich trivialen Code in eine Funktion einfügen und den Asm dafür sehen können. (Ich denke, dies wurde zu einem Streit über die Definition von “trivial”, was nicht das war, was ich beabsichtigt hatte …). In Ihrem Beispiel wurde zum Generieren ein Funktionsaufruf verwendet i und j. Mein Beispiel wäre zu machen i und j Funktion Parameteralso würden sie einfach am Anfang des Codes für die Funktion in Registern sitzen.

    – Peter Cordes

    10. November 2015 um 23:03 Uhr


  • Die Lösung besteht darin, in der Compilerausgabe nach Funktionen zu suchen, die mit Parametern arbeiten, anstatt main() Betrieb mit Kompilierzeitkonstanten. Hier ist zB ein einfaches Beispiel für die Summierung eines Float-Arrays mit der Ausgabe von gcc asm von godbolt: goo.gl/RxIFEF

    – Peter Cordes

    10. November 2015 um 2:28 Uhr

  • Ich glaube, das habe ich gesagt: “Sie müssten sich die Assembly-Ausgabe für Ihren speziellen, nicht trivialen Code ansehen”. 😉

    – Scott

    10. November 2015 um 22:08 Uhr

  • Ich habe versucht zu sagen, dass Sie ziemlich trivialen Code in eine Funktion einfügen und den Asm dafür sehen können. (Ich denke, dies wurde zu einem Streit über die Definition von “trivial”, was nicht das war, was ich beabsichtigt hatte …). In Ihrem Beispiel wurde zum Generieren ein Funktionsaufruf verwendet i und j. Mein Beispiel wäre zu machen i und j Funktion Parameteralso würden sie einfach am Anfang des Codes für die Funktion in Registern sitzen.

    – Peter Cordes

    10. November 2015 um 23:03 Uhr


1417610cookie-checkVerlangsamen temporäre Variablen mein Programm?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy