Und jetzt das ganze noch einmal mit lokal definierter Variable:
void foo1(void) { int TimingDelay; TimingDelay=5; return --TimingDelay; }
void foo2(void) { int TimingDelay;
TimingDelay=5; return TimingDelay--; }
a=foo1(); hier ist a=4 b=foo2(); hier erwarte ich b=5 aber tatsächlich wird wohl b=4 zurückgegeben?
M.
Didn't find your answer? Ask the community — no account required.
J
Johannes Bauer
Dir ist doch jetzt schon mehrfach erklärt worden, dass der Compiler den Temporary wegoptimiert, wenn er nicht verwendet wird. Was ist daran so schwer zu verstehen? Hast du's mal durch einen Compiler gejagt und dir das Assembly angesehen?
Im übrigen hast Stroustroup bei C nichts zu melden und C kennt auch weder Templates noch Referenzen; beides sind Features von C++.
Gruß, Johannes
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa
08048400 : 8048400: a1 18 a0 04 08 mov 0x804a018,%eax 8048405: 85 c0 test %eax,%eax 8048407: 74 08 je 8048411 8048409: 83 e8 01 sub $0x1,%eax 804840c: a3 18 a0 04 08 mov %eax,0x804a018 8048411: f3 c3 repz ret
In allen Fällen (!) exakt dieselbe Anzahl an Instruktionen. gcc 4.6.3 für Intel und gcc 4.8.1 für ARM.
Gruß, Johannes
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa
T
Torsten Schneider
Das wäre dann aber ein Compilerbug.
#include
int foo2() { int lokal = 5;
return lokal--; }
int main() { printf("foo2: %d\n", foo2());
return 0; }
Das liefert als Ergebnis:
foo2: 5
Also wie erwartet (gcc 4.6.3)
Viele Grüße, Torsten
E
Edzard Egberts
Auf der C++-Ebene ist das eindeutig.
Das ist zwar richtig, dann aber ein anderer Code. Ich habe ja von Anfang an gesagt, dass es wegen der Optimierung beim Compilieren wahrscheinlich egal ist, welchen Operator man nimmt. Tatsache ist eben nur, dass diese Operatoren unterschiedlich implementiert sind und der Postfix-Operator in der nicht optimierten Implementierung einen zusätzlichen Schritt erfordert.
E
Edzard Egberts
Matthias Weingart schrieb:
int foo1(void)
int foo2(void)
Nein, b ist tatsächlich gleich 5 - der Rückgabewert wird vor dem Dekrementieren ermittelt. Nicht optimiert würde der Code dann noch die lokale Variable dekrementieren, bevor sie verworfen wird.
Ob "TimingDelay" lokal oder global angelegt ist, macht keinen Unterschied, da die Rückgabe als Wert stattfindet. Für die globale Variable könnte man allenfalls beim Präfix-Operator ein return by Reference machen.
A
Axel Schwenke
Dito!
Und ich verstehe nicht, was so schwer daran ist einzusehen, daß diese spezielle "Optimierung" seit gut 10 Jahren obsolet ist.
So lange der Wert nicht verwendet wird, kann *jeder* Compiler den man heute überhaupt verwenden wöllte, den temporary wegoptimieren. In den Anfangstagen von C++, als Stroustrup sein Buch geschrieben hat, mag da was dran gewesen sein. Mittlerweile nicht mehr.
Und um nochmal zum Originalkontext zurückzukommen: die Funktion ist eine ISR und hat folglich keinen Returnwert. Aus semantischer Sicht sind Prefix- und Postfix-Dekrement also gleichwertig.
XL
E
Edzard Egberts
Nein, das habe ich selber schon von Anfang an gesagt, musste mir also nicht erklärt werden.
Du verstehst nicht, dass ich lieber Code schreibe, der nicht noch "by magic" optimiert werden muss, sondern schon vorher genau das macht, was er tun soll.
Ja, deshalb setzen sich C-Programmierer nicht mit solchen Details auseinander, das lernt man dann beim Überladen von Operatoren für eigene Klassen.
M
Matthias Weingart
Edzard Egberts :
Aber in deinem Posting vom 20.6. 15:54 schreibst du etwas anderes, bist schön in die Falle getappt ;-(. Ziemlich irreführend das alles (geht mir aber genauso).
M.
A
Axel Schwenke
Sehr richtig. Das macht auch nochmal deutlich, daß der Programmierer der Originalvariante sich durch die Verwendung des ?-Operators nur selber ins Knie schießt. Denn aus programmlogischer Sicht ist die Zuweisung der 0 nicht notwendig (und nein, das ist erkennbar kein SFR, wo die Zuweisung gewollte Seiteneffekte haben könnte).
Die Zuweisung ist nur deswegen notwendig, weil der ?-Operator *immer* beide Zweige enthalten muß. Und durch die volatile Deklaration der Variable kann der Compiler das noch nicht mal wegoptimieren.
Schlußfolgerung: der ?-Operator mag einen höheren Coolness-Faktor haben als ein schlichtes if(). Aber man sollte darauf aufpassen, nicht vor lauter Coolness gleich auf die Nase zu fallen.
XL
M
Matthias Weingart
Edzard Egberts :
Naja ich schreib lieber Code, der gut verständlich ist; selbst wenn er auch mal nicht optimal sein sollte. Die geringere Anzahl von Fehlern ist mir da einfach mehr wert. (Meistens wird das aber heutzutage von den guten Compilern sowieso noch optimiert). Ausnahme ist lediglich - wenn es nicht anders geht und die Speed oder Codegrösse gebraucht wird. M.
E
Edzard Egberts
Ich sehe das nicht als "Optimierung" - mir stehen zwei ähnliche Operatoren zur Verfügung und ich wende den an, der genau das gewünschte Resultat bringt. Irgendetwas schreiben und dann hoffen, dass der Compiler es schon hinbiegt, ist einfach nicht meine Arbeitsweise.
Stimmt, aber das bedeutet, der Code der läuft, ist nicht der Code, den ich geschrieben habe. Ich denke nicht, dass das unbedingt ein Merkmal guter Software ist.
E
Edzard Egberts
Matthias Weingart schrieb:
Guck' Dir das bitte noch einmal genau an. Im genannten Posting ist die globale Variable 4, weil sie im Gegensatz zur lokalen Variable nach dem Dekrement nicht verworfen wird. Du verwechselst gerade den Inhalt der Variablen mit dem Rückgabewert.
T
Torsten Schneider
Naja, nicht optimierten Code zu bemängeln ist müßig. Oft machen die Compiler das bewusst nach Schema F, weil sich nicht optimierter Code besser debuggen lässt, da man in der Regel dann den Assemblercode besser einzelnen Sourcecodezeilen zuordnen kann.
In dem hier gezeigten Beispiel wird sogar noch viel mehr optimiert, zumindest beim gcc 4.6.3 mit Optimizerflag -O2. Der Aufruf der Funktion foo2() wird im Assemblercode schlichtweg durch die Konstante 5 ersetzt.
Bei einer lokalen Variable wird das Postdekrement ohnehin wegoptimiert, da sinnlos.
Viele Grüße, Torsten
J
Johannes Bauer
Naja, also eine deart offensichtliche Optimierung, die jeder nicht hirntote Compiler heutzutage zu Stande bringt, würde ich nicht als "Magic" bezeichnen. Wohl aber deine Heransgehensweise an das Schreiben von Code als "premature optimization", ein typisches Antipattern das oft noch zu schlechterem Code führt, als wenn man die Optimierung dem Compiler überlassen hätte.
Lesbarkeit und Wartbarkeit steht an erster Stelle; Performanz da, wo man sie braucht. Ich halte es für viel besser, zu wissen, was der Compiler tut, statt zu versuchen, klüger als der Compiler zu sein. Das funktioniert ohnehin nur selten. Ein bischen in die Jahre gekommen, aber trotzdem sehr lesenswert:
formatting link
Unsinn. Denn der Unterschied zwischen einen Pre- und Postinkrement wird eben ohnehin erst dann wirklich performancetechnisch relevant, wenn es sich nicht mehr um primitive Datentypen handelt, sondern um (möglicherweise große) Objekte. Die gibt es in C nicht, daher ist das Problem nicht existent. Jeder dahergelaufene C-Programmierer weiß, dass der Unterschied zwischen pre- und postinkrement auf primitiven Datentypen völlig vernachlässigbar ist.
Gruß, Johannes
>> Wo hattest Du das Beben nochmal GENAU vorhergesagt?
> Zumindest nicht öffentlich!
Ah, der neueste und bis heute genialste Streich unsere großen
Kosmologen: Die Geheim-Vorhersage.
- Karl Kaos über Rüdiger Thomas in dsa
M
Michael Baeuerle
Ich finde es ist trotzdem der richtige Ansatz den Code (wie Edzard es vorschlug) so hinzuschreiben wie man es eigentlich meint und nicht darauf zu hoffen/vertrauen, dass der Optimizer es hinterher schon geradebiegen wird. Speziell wenn es so einfach geht wie in diesem Fall und die bessere Variante nichtmal hässlicher aussieht.
Bei Microcontrollern möchte man den Optimizer bei LowLevel-Kram auch manchmal lieber ganz abschalten. Beliebtes Beispiel beim GCC für avr ist, dass der Optimizer die Reihenfolge der Befehle bei der globalen Interrupt Steuerung in unerwünschter Weise ändert (weil er das darf und nicht versteht was er damit ggf. anrichtet):
formatting link
Das ist kein Bug und da hilft kein volatile und keine barriers, nur -O0 hilft wirklich ... und dann tut es schon weh wenn man den Sourcecode nicht "richtig" geschrieben hat.
Micha
B
Bernd Laengerich
Am 21.06.2013 11:46, schrieb Michael Baeuerle:
Ich habe bisher nur sehr wenig mit gcc und avr gearbeitet, aber sollten da nicht für die kritischen Stellen mittels #pragma geschützte Bereiche oder Funktionsattribute sinnvoller sein?
Bernd
C
Christian Zietz
Bernd Laengerich schrieb:
Da man afaik beim gcc per Funktionsattribut die angewendeten Optimierungen für einzelne Funktionen bestimmen kann, ist es sicher sinnvoll, in dem von Michael geschilderten Fall für die betroffene Funktion nur das Umsortieren abzuschalten. -O0 für die gesamte Datei wäre mit Kanonen auf Spatzen geschossen.
Mein Ansatz wäre zusätzlich noch der, daß man damit innerhalb der Datei exakt steuern und dokumentieren kann, wie der Abschnitt behandelt werden soll. Das wird auch der Nachfolger in der Firma nicht ohne weiteres ändern, während ein Optimierungsschalter im Makefile eher mal pauschal umgestellt wird.
Bernd
Join the Discussion
Have something to add? Share your thoughts — no account required.
Didn't find your answer?
Ask the community — no account required
Report Content
You are reporting this content to the moderators. They will look at it
ASAP.