Winavr, merkwürdiger Code

Edzard Egberts :

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.

Reply to
Matthias Weingart
Loading thread data ...

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! 
 Click to see the full signature
Reply to
Johannes Bauer

Der Vollständigkeit halber:

int x;

void postfix(void) { if (x) x--; }

void prefix(void) { if (x) --x; }

int main() { return 0; }

ARM Cortex-M0:

00008004 : 8004: 4b03 ldr r3, [pc, #12] ; (8014 ) 8006: b500 push {lr} 8008: 681a ldr r2, [r3, #0] 800a: 2a00 cmp r2, #0 800c: d001 beq.n 8012 800e: 3a01 subs r2, #1 8010: 601a str r2, [r3, #0] 8012: bd00 pop {pc} 8014: 0001002c .word 0x0001002c

00008018 : 8018: 4b03 ldr r3, [pc, #12] ; (8028 ) 801a: b500 push {lr} 801c: 681a ldr r2, [r3, #0] 801e: 2a00 cmp r2, #0 8020: d001 beq.n 8026 8022: 3a01 subs r2, #1 8024: 601a str r2, [r3, #0] 8026: bd00 pop {pc} 8028: 0001002c .word 0x0001002c

x86_64:

00000000004004f0 : 4004f0: 8b 05 32 0b 20 00 mov 0x200b32(%rip),%eax # 601028 4004f6: 85 c0 test %eax,%eax 4004f8: 74 09 je 400503 4004fa: 83 e8 01 sub $0x1,%eax 4004fd: 89 05 25 0b 20 00 mov %eax,0x200b25(%rip) # 601028 400503: f3 c3 repz retq 400505: 66 66 2e 0f 1f 84 00 data32 nopw %cs:0x0(%rax,%rax,1) 40050c: 00 00 00 00

0000000000400510 : 400510: 8b 05 12 0b 20 00 mov 0x200b12(%rip),%eax #

601028 400516: 85 c0 test %eax,%eax 400518: 74 09 je 400523 40051a: 83 e8 01 sub $0x1,%eax 40051d: 89 05 05 0b 20 00 mov %eax,0x200b05(%rip) # 601028 400523: f3 c3 repz retq

i386:

080483e0 : 80483e0: a1 18 a0 04 08 mov 0x804a018,%eax 80483e5: 85 c0 test %eax,%eax 80483e7: 74 08 je 80483f1 80483e9: 83 e8 01 sub $0x1,%eax 80483ec: a3 18 a0 04 08 mov %eax,0x804a018 80483f1: f3 c3 repz ret 80483f3: 8d b6 00 00 00 00 lea 0x0(%esi),%esi 80483f9: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi

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! 
 Click to see the full signature
Reply to
Johannes Bauer

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

Reply to
Torsten Schneider

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.

Reply to
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.

Reply to
Edzard Egberts

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

Reply to
Axel Schwenke

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.

Reply to
Edzard Egberts

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.

Reply to
Matthias Weingart

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

Reply to
Axel Schwenke

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.

Reply to
Matthias Weingart

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.

Reply to
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.

Reply to
Edzard Egberts

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

Reply to
Torsten Schneider

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! 
 Click to see the full signature
Reply to
Johannes Bauer

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

Reply to
Michael Baeuerle

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

Reply to
Bernd Laengerich

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.

Christian

--
Christian Zietz  -  CHZ-Soft  -  czietz (at) gmx.net 
WWW: http://www.chzsoft.de/ 
 Click to see the full signature
Reply to
Christian Zietz

Am 21.06.2013 13:32, schrieb Christian Zietz:

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

Reply to
Bernd Laengerich

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.