Forum: Compiler & IDEs sei nicht schlauer als der Compiler?


von Adib (Gast)


Lesenswert?

Hallo Leute,

es gibt ja so die Regeln beim Optimieren, dass man nicht schlauer sein 
sollte als der Compiler und der Compiler schon so optimiert.

meine Frage:
soll ich jetzt lieber so schreiben:
1
#define LOOPS (10)
2
for(int loop = 0; loop < LOOPS; loop++) {
3
    // loop variable nicht verwendet
4
}

oder eher so (handoptimiert:
1
#define LOOPS (10)
2
for(int loop = LOOPS; loop != 0; loop--) {
3
    // loop variable nicht verwendet
4
}

Mir ist es eher egal. Lesbarer finde ich die erste Variante.
Wann aber im Code durch unterschiedliche Programmierer mal so mal so 
gemacht wird, finde ich das eher lästig und das stört mich schon.

von Pech gehabt (Gast)


Lesenswert?

Tja, die Anzahl der Schleifendurchläufe ist in beiden Varianten 
unterschiedlich...

von Karl Rammer (Gast)


Lesenswert?

Nein, 2x 10 Durchläufe.

von Christian S. (christian_s593)


Lesenswert?

aus:
1
volatile uint8_t i = 0;
2
int main (void){
3
#define LOOPS (10)
4
 for(int loop = LOOPS; loop != 0; loop--) {
5
    i++;
6
 }
7
}
wird:
1
main:
2
        ldi r24,lo8(10)
3
        ldi r25,hi8(10)
4
.L2:
5
        lds r18,i
6
        subi r18,lo8(-(1))
7
        sts i,r18
8
        sbiw r24,1
9
        brne .L2
10
        ldi r24,lo8(0)
11
        ldi r25,hi8(0)
12
        ret
13
i:

und aus:
1
volatile uint8_t i = 0;
2
int main (void){
3
#define LOOPS (10)
4
 for(int loop = 0; loop < LOOPS; loop++) {
5
   i++;
6
 }
7
}
wird das:
1
main:
2
        ldi r24,lo8(10)
3
        ldi r25,hi8(10)
4
.L2:
5
        lds r18,i
6
        subi r18,lo8(-(1))
7
        sts i,r18
8
        sbiw r24,1
9
        brne .L2
10
        ldi r24,lo8(0)
11
        ldi r25,hi8(0)
12
        ret
13
i:

Mit AVR gcc 4.6.4 und -O1 getestet im Compiler Explorer.
Bei -O3 ist das Gleiche mit ausgerollter Schleife.
Denke die Frage ist damit geklärt, der Compiler macht ein interessanten 
Job. Hab mich da auch immer gefragt, wie man Schleifen am Besten für den 
Compiler vorbereitet. Jetzt kenn ich die Antwort.

von Peter D. (peda)


Lesenswert?

Adib schrieb:
> Lesbarer finde ich die erste Variante.

Das kann man nicht verallgemeinern. Viele finden die 2. Variante 
lesbarer.
Ich hab mit Assembler angefangen (8051), da ist quasi die 2. Variante 
der DJNZ-Befehl.

Als ich mit C angefangen hatte, habe ich mich mit der 1. Variante oft um 
+/-1 verzählt. Ich mußte immer erst überlegen, ist der Startwert 0 oder 
1 und muß ich auf < oder <= vergleichen.
Bei der 2. Variante gibt es keine Fehlermöglichkeit, die Anzahl 
Durchläufe ist der Initialwert.
Und falls die Anzahl keine Konstante, sondern eine Variable ist, muß man 
nicht erst überlegen, ob sie sich während der Schleife ändern könnte. 
Wenn der Compiler das nicht mehr erkennen kann, muß er für die erste 
Variante den längeren Code erzeugen.

: Bearbeitet durch User
von g457 (Gast)


Lesenswert?

> Bei der 2. Variante gibt es keine Fehlermöglichkeit, die Anzahl
> Durchläufe ist der Initialwert.

Nicht ganz, zur allgemeinen Erheiterung kann man auch auf '>= 0' testen.

von Dieter F. (Gast)


Lesenswert?

Peter D. schrieb:
> Viele finden die 2. Variante
> lesbarer.

Kommt drauf an, was man damit bezweckt. Wenn man mit Arrays arbeitet 
können beide Varianten sinnvoll sein. Generell zähle ich aber lieber 
hoch als runter - wohl ein Relikt aus der Kindheit :-) - obwohl ich mal 
Astronaut werden wollte :-\

von Falk B. (falk)


Lesenswert?

@ Adib (Gast)

>Mir ist es eher egal. Lesbarer finde ich die erste Variante.

Dann nimm sie. Solche Mikrooptimierungen sind zu 99% Unsinn bis 
kontraproduktiv.

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

Wenn man WIRKLICH das letzte Prozent an CPU-Leitung rausholen MUSS, 
macht man das so oder so besser in Assembler, sinnvollwerweise in einer 
separaten Funktion/Datei. Meistens erreicht man durch gescheites Design 
deutlich mehr.

von Berufsrevolutionär (Gast)


Lesenswert?

Christian S. schrieb:

>
1
>  for(int loop = 0; loop < LOOPS; loop++) {
2
>    i++;
3
>  }
4
>
> wird das:
>
1
> main:
2
>         ldi r24,lo8(10)
3
>         ldi r25,hi8(10)
4
> .L2:
5
>         lds r18,i
6
>         subi r18,lo8(-(1))
7
>         sts i,r18
8
>         sbiw r24,1
9
>         brne .L2
10
>         ldi r24,lo8(0)
11
>         ldi r25,hi8(0)
12
>         ret
13
> i:
14
>

Naja hier ist der Compiler genauso unsmart wie der User.
Integer ist der falsche Typ (16 bit) für den Schleifenindex 
(Wertebereich 10 bis 0), dadurch wird der Code langsamer und größer als 
nötig.
char ist die bessere Wahl. auch hinsichtlich der höheren Zahl von
Speicherstellen für 16 bit Werte.

von Vn N. (wefwef_s)


Lesenswert?

Berufsrevolutionär schrieb:
> Integer ist der falsche Typ (16 bit) für den Schleifenindex
> (Wertebereich 10 bis 0), dadurch wird der Code langsamer und größer als
> nötig.
> char ist die bessere Wahl. auch hinsichtlich der höheren Zahl von
> Speicherstellen für 16 bit Werte.

Kann man so allgemein nicht sagen, je nach Architektur kann int auch 
schneller sein (viele 32 Bit Architekturen).
Allerdings sollten Schleifenzähler grundsätzlich unsigned sein, wenn 
nämlich die Anzahl der durchläufe zur Compilezeit nicht bekannt ist, tut 
sich der Compiler mit unsigned xyz nämlich wesentlich leichter bei der 
Optimierung.
char ist für Zählvariablen sowieso ungeeignet, da im Standard nicht 
definiert ist, ob signed oder unsigned 
(http://www.trilithium.com/johan/2005/01/char-types/).
Die richtige Wahl wäre uint_fast8_t: dieser ist definiert als 
schnellster unsigned Datentyp mit mindestens 8 Bit Breite.
Will man (aus welchen Gründen auch immer) stdint.h nicht nutzen, wäre 
unsigned char Mittel der Wahl.

von Volle (Gast)


Lesenswert?

Ein ordentlicher Compiler versucht die meist vorhandenen 
Schleifenhardware zu verwenden. Die kann in der Regel entweder nur 
inkrementieren oder dekrementieren. Der Compiler dreht die Zählrichtung 
dann, falls nötig.

Die meisten Prozessoren haben Schleifenzähler auf Registergröße.
Kleinere oder größere Zählvariablen erzeugen dann Mehraufwand.

Oft ist ja Aritmetik und Adresseinheit getrennt von der Schleifeneinheit
Dann sollte man den Schleifenzähler nicht für solche Operationen 
verwenden.
Sondern eine eigenen lokale Variable.
Das gilt dann auch für den Arrayindex.

Hängt also ganz klar vom Zielsystem ab und wie gut es der Compiler 
unterstützt.

Aber generelle gilt schon das man nichts unnötiges erzwingen soll.

von Oliver S. (oliverso)


Lesenswert?

Ein richtig ordentlicher Compiler macht bei 10 Schleifendurchläufen loop 
unrolling...

Oliver

von Markus F. (mfro)


Lesenswert?

Wer an Schleifen mit zehn Durchläufen rumoptimiert, hat entweder sonst 
nix zu tun oder - mit Verlaub - ganz schön einen an der Klatsche.

von Volle (Gast)


Lesenswert?

Oliver S. schrieb:
> Ein richtig ordentlicher Compiler macht bei 10 Schleifendurchläufen loop
> unrolling...
>
> Oliver

den Compiler würde ich in die Tonne werfen.

hab gerade einen Schleife über 5
die aber 8000/s aufgerufen wird
da tut jeder Befehl mehr richtig schmerzen.

Der Compiler kann die 8000/s gar nicht wissen

von (prx) A. K. (prx)


Lesenswert?

Volle schrieb:
> Oliver S. schrieb:
>> Ein richtig ordentlicher Compiler macht bei 10 Schleifendurchläufen loop
>> unrolling...
>>
>> Oliver
>
> den Compiler würde ich in die Tonne werfen.

Kann ich nicht nachvollziehen.

> hab gerade einen Schleife über 5
> die aber 8000/s aufgerufen wird
> da tut jeder Befehl mehr richtig schmerzen.

Loop unrolling vergrössert zwar den Code, aber reduziert meist die 
Anzahl ausgeführter Befehle. Cache-Effekte aussen vor gelassen ist es 
also nicht notwendigerweise hinderlich. Zumal sequentiell ausgeführter 
Code bei µCs meist schneller ist als nichtsequentieller Code.

: Bearbeitet durch User
von Volle (Gast)


Lesenswert?

A. K. schrieb:
> Volle schrieb:
>> Oliver S. schrieb:
>>> Ein richtig ordentlicher Compiler macht bei 10 Schleifendurchläufen loop
>>> unrolling...
>>>
>>> Oliver
>>
>> den Compiler würde ich in die Tonne werfen.
>
> Kann ich nicht nachvollziehen.
>
>> hab gerade einen Schleife über 5
>> die aber 8000/s aufgerufen wird
>> da tut jeder Befehl mehr richtig schmerzen.
>
> Loop unrolling vergrössert zwar den Code, aber reduziert die Anzahl
> ausgeführter Befehle, mindestens wenn die Anzahl Durchläufe bekannt ist.
> Cache-Effekte aussen vor gelassen ist es also nicht hinderlich. Zumal
> sequentiell ausgeführter Code bei µCs meist schneller ist als
> nichtsequentieller Code.

die meisten können Befehle schneller abarbeiten als aus dem Flash laden
weshalb Cache gesetzt ist
mehrstufige Pipelines, parallele Pipelines, Loop Hardware
für all das ist sequenzieller Code schlecht


vor 20 Jahren konnten die Controller  mit sequenziellem Code leichter 
umgehen. Seither hat sich aber viel geändert

von Feldstecher (Gast)


Lesenswert?

Volle schrieb:
> hab gerade einen Schleife über 5
> die aber 8000/s aufgerufen wird
> da tut jeder Befehl mehr richtig schmerzen.
Und genau deshalb ist Loop Unrolling hier angebracht. Deine Schleife 
wird dadurch schneller durchlaufen. Pro Sekunde sogar 8000 Mal 
'schneller'.

von (prx) A. K. (prx)


Lesenswert?

Volle schrieb:
> die meisten können Befehle schneller abarbeiten als aus dem Flash laden
> weshalb Cache gesetzt ist

Die meisten µCs ohne Cache können Flash schnell genug sequentiell laden 
um nicht wesentlich ausgebremst zu werden. Manche STM32 sind da zwar 
hart an der Kante gebaut - nur sind genau die dann nichtsequentiell 
mangels Cache langsamer.

Bei Prozessoren mit Cache darf man oft davon ausgehen, dass der Inhalt 
von sehr oft ausgeführtem Code (8000/s) im Cache verbleibt. Das ist der 
Sinn eines Caches. Cache bremst sequentiellen Code nicht aus, so lange 
unrolling nicht so krass gefahren wird, dass der Cache zu klein wird.

> mehrstufige Pipelines, parallele Pipelines, Loop Hardware
> für all das ist sequenzieller Code schlecht

Auch bei vielen Highend-CPUs der letzten Jahre führt nichtsequentielle 
Ausführung zu einer kleinen Verzögerung im Fetch und zu geringerem 
Befehlsdurchsatz, wenn fetch/decode/dispatch/retire den Durchsatz 
begrenzt.

> Seither hat sich aber viel geändert

Ich bin auch nicht (nur) von gestern. ;-)

von Stefan K. (stefan64)


Lesenswert?

Volle schrieb:
> die meisten können Befehle schneller abarbeiten als aus dem Flash laden
> weshalb Cache gesetzt ist
> mehrstufige Pipelines, parallele Pipelines, Loop Hardware
> für all das ist sequenzieller Code schlecht

Kannst Du das konkretisieren?
Auf welche Controller beziehst Du Dich konkret?

> vor 20 Jahren konnten die Controller  mit sequenziellem Code leichter
> umgehen. Seither hat sich aber viel geändert

Meiner Meinung nach ist das Gegenteil der Fall.
Früher war ein Flash-Zugriff ein echter Flaschenhals. Mittlerweile ist 
das Flash bei vielen Controllern sehr breit organisiert (STM32F4xx: 128 
Bits). Unter anderem dadurch werden Peformance-Verluste durch 
Flash-Waitstaites minimiert.

Viele Grüße, Stefan

von Guardians of the memory space (Gast)


Lesenswert?

vn n. schrieb:
> Kann man so allgemein nicht sagen, je nach Architektur kann int auch
> schneller sein (viele 32 Bit Architekturen).

Hier ist es ein 8bit Controller AVR, dessen Register 8 bit breit sind. 
also ist ein 8bit type angebracht. Integer wird wie im assemblerlisting 
zu sehen auf 16bit umgesetzt. Was nicht nur zwei statt einem Befehl bei 
der Initialisierung benötigt, sondern auch eines der wenigen 16bit 
Registerpaare blockiert, was bei komplexeren Operationen den Compiler 
zwingen könnte auf SRAM (Stack) für Variablen zurückzugreifen.

> Die richtige Wahl wäre uint_fast8_t: dieser ist definiert als
> schnellster unsigned Datentyp mit mindestens 8 Bit Breite.

Man lernt nie aus -> ein schneller 8bit Typ -> was es nicht alles in 
einer Hochsorache gibt. :-0
Soll das ein 8bit Register sein, im unterschied zu einer 8bit 
RAM-addresse (auch 8 bit aber Langsam)?

Abgesehen ob fast oder slow-Typen bevorzuge ich auch einen type der die 
(register-)breite in bits erkennen lässt, statt char, integer etc. was 
je nach Architektur unterschiedlich lang ist. Spannend wird's beim ARM 
mit seinem THUMB-Instruction_set/mode, da wechselt die word-breite 
innerhalb der Architectur.

von Oliver S. (oliverso)


Lesenswert?

Von AVR ist beim TO niemals die Rede.

Oliver

von Markus F. (mfro)


Lesenswert?

Guardians of the memory space schrieb:
> Soll das ein 8bit Register sein, im unterschied zu einer 8bit
> RAM-addresse (auch 8 bit aber Langsam)?

Nein.

Das ist der schnellste Typ auf der Plattform mit einem Wertebereich von 
mindestens 8 Bit. Kann also auch 16 oder 32 oder 64 oder ... Bit 
haben.

Was nutzt es dir, mit 8 Bit zu rechnen, wenn die Plattform bei jeder 
arithmetischen Operation erst mal eine Sign-Extension machen (und 
hinterher wieder "abschneiden") muß, weil sie (z.B.) nur 32-Bit 
Operationen kennt?

Stell' dir vor, es gibt tatsächlich auch eine Welt außerhalb von AVR ;)

von Volle (Gast)


Lesenswert?

A. K. schrieb:
> Volle schrieb:
>> die meisten können Befehle schneller abarbeiten als aus dem Flash laden
>> weshalb Cache gesetzt ist
>
> Die meisten µCs ohne Cache können Flash schnell genug sequentiell laden
> um nicht wesentlich ausgebremst zu werden. Manche STM32 sind da zwar
> hart an der Kante gebaut - nur sind genau die dann nichtsequentiell
> mangels Cache langsamer.

da kenne ich viele Gegenbeispiele ( Freescale, Infineon) ein 160Mhz Risc 
ist schneller als das beste Flash
von Multicore will ich gar nicht erst anfangen


> Bei Prozessoren mit Cache darf man oft davon ausgehen, dass der Inhalt
> von sehr oft ausgeführtem Code (8000/s) im Cache verbleibt. Das ist der
> Sinn eines Caches. Cache bremst sequentiellen Code nicht aus, so lange
> unrolling nicht so krass gefahren wird, dass der Cache zu klein wird.

nur wenn du kein Multitasking Betriebssystem hast.
die Caches bei µC sind je recht einfach aufgebaut und nicht mehrfach 
assoziativ

>
>> mehrstufige Pipelines, parallele Pipelines, Loop Hardware
>> für all das ist sequenzieller Code schlecht
>
> Auch bei vielen Highend-CPUs der letzten Jahre führt nichtsequentielle
> Ausführung zu einer kleinen Verzögerung im Fetch und zu geringerem
> Befehlsdurchsatz, wenn fetch/decode/dispatch/retire den Durchsatz
> begrenzt.
>
zero overhead loop haben die meisten modernen CPUs
parallel zu Arithmetik  Load/Store ausführen bringt erst in der Schleife 
richtig Gewinn

von Vn N. (wefwef_s)


Lesenswert?

Guardians of the memory space schrieb:
> Hier ist es ein 8bit Controller AVR, dessen Register 8 bit breit sind.

Erstens: nein, der TS hat AVR nicht erwähnt.
Zweitens: mein Posting nochmal lesen, ich hab geschrieben, dass man es 
Allgemein nicht sagen kann, für konkrete Architekturen allerdings 
stimmen kann.

Guardians of the memory space schrieb:
> Man lernt nie aus -> ein schneller 8bit Typ -> was es nicht alles in
> einer Hochsorache gibt. :-0
> Soll das ein 8bit Register sein, im unterschied zu einer 8bit
> RAM-addresse (auch 8 bit aber Langsam)?

Nein, schlichtweg ein typedef auf den schnellsten Typ, der mindestens x 
Bit breit ist. Auf einem 32-Bit Processor kann dann ein uint_fast8_t 
auch mal 32 Bit breit sein (ARM z.B.).
https://stackoverflow.com/a/35055175/7051705

von Teo D. (teoderix)


Lesenswert?

Guardians of the memory space schrieb:
> Man lernt nie aus -> ein schneller 8bit Typ -> was es nicht alles in
> einer Hochsorache gibt. :-0
> Soll das ein 8bit Register sein, im unterschied zu einer 8bit
> RAM-addresse (auch 8 bit aber Langsam)?

Mein Gott, das hab ja sogar ich gerafft.

vn n. schrieb:
> Die richtige Wahl wäre uint_fast8_t: dieser ist definiert als
> schnellster unsigned Datentyp mit *mindestens 8 Bit Breite*

Eine 32bit Cpu wird da eher nicht mit 8bit rumwuschteln und eine 8bit 
Cpu sicher nicht mit 16bit....

von (prx) A. K. (prx)


Lesenswert?

Volle schrieb:
> zero overhead loop haben die meisten modernen CPUs

Nur macht das eine Schleife nicht schneller als unrolled Code ohne Test 
und Sprungbefehl.

Wobei auch ein Cortex M7 eine moderne CPU ist, und um diese Kategorie 
geht es hier häufiger als um einen Sky Lake.

> parallel zu Arithmetik  Load/Store ausführen bringt erst in der Schleife
> richtig Gewinn

Kann ich nicht nachvollziehen. Wenn man den Overhead einer Schleife 
aussen vor lässt, weil zero overhead loop, dann besteht ansonsten kein 
Unterschied zwischen sequentieller und nichtsequentieller Version. Es 
sind genau die gleichen Befehle in genau der gleichen präsentieren 
Reihenfolge. Weshalb sollte also die nichtsequentielle Version langsamer 
sein? Selbst mit microop/trace cache ändert das nichts, so lange der 
Code reinpasst.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Adib schrieb:
> Hallo Leute,
>
> es gibt ja so die Regeln beim Optimieren, dass man nicht schlauer sein
> sollte als der Compiler und der Compiler schon so optimiert.
>
> meine Frage:
> soll ich jetzt lieber so schreiben:
>
1
> #define LOOPS (10)
2
> for(int loop = 0; loop < LOOPS; loop++) {
3
>     // loop variable nicht verwendet
4
> }
5
>

Obwohl nicht explizit hier erwähnt, vermute ich, dass es sich um C 
handelt.

Sollte es C++ sein, so sollte man sich ++loop statt loop++ angewöhnen, 
falls man den urspünglichen Zustand nicht braucht.
Auch wenn es für fundamentale Typen keinen Unterschied macht: in 
generischem Code kann das post-increment langsamer sein als das 
pre-increment, bspw. wenn der Typ von loop nicht int sondern A ist.

von Markus F. (mfro)


Lesenswert?

Wilhelm M. schrieb:
> Obwohl nicht explizit hier erwähnt, vermute ich, dass es sich um C
> handelt.

das ist entweder C++ oder C99 +

Vorher durfte man die Laufvariable nicht im for-Statement deklarieren.

von Wilhelm M. (wimalopaan)


Lesenswert?

Markus F. schrieb:
> Wilhelm M. schrieb:
>> Obwohl nicht explizit hier erwähnt, vermute ich, dass es sich um C
>> handelt.
>
> das ist entweder C++ oder C99 +

... C90/C99/C11 spielt aber für meine Anmerkung keine Rolle.

von Oliver S. (oliverso)


Lesenswert?

C99 ist immerhin schon aus dem letzten Jahrtausend, da kann man das 
schon mal voraussetzen.

Oliver

von (prx) A. K. (prx)


Lesenswert?

Volle schrieb:
>> Die meisten µCs ohne Cache können Flash schnell genug sequentiell laden
>> um nicht wesentlich ausgebremst zu werden.
>
> da kenne ich viele Gegenbeispiele ( Freescale, Infineon) ein 160Mhz Risc
> ist schneller als das beste Flash

Die haben trotz 160MHz weder Cache noch ausreichend breiten Prefetch?

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Oliver S. schrieb:
> C99 ist immerhin schon aus dem letzten Jahrtausend, da kann man das
> schon mal voraussetzen.
>
> Oliver

Schon mal bei Microsoft über'n Zaun geguckt? Mein Rat: laß' es ;)

von Guardians of the memory space (Gast)


Lesenswert?

Oliver S. schrieb:
> Von AVR ist beim TO niemals die Rede.

Aber bei dem einzigen belastbaren Compiler-Output in diesem Thread und 
der TO schließt AVR nicht explizit aus -> die (einzige?) 
Optimierungsmöglichkeit in diesem Code liegt in der richtigen type 
Auswahl. Die falsche Auswahl des Types kann auch nicht durch 
Compilerschalter behoben werden.

von (prx) A. K. (prx)


Lesenswert?

Guardians of the memory space schrieb:
>> Von AVR ist beim TO niemals die Rede.
>
> Aber bei dem einzigen belastbaren Compiler-Output in diesem Thread und
> der TO schließt AVR nicht explizit aus

Der hat sich seither allerdings auch nicht mehr blicken lassen.

von Volle (Gast)


Lesenswert?

A. K. schrieb:
> Volle schrieb:
>>> Die meisten µCs ohne Cache können Flash schnell genug sequentiell laden
>>> um nicht wesentlich ausgebremst zu werden.
>>
>> da kenne ich viele Gegenbeispiele ( Freescale, Infineon) ein 160Mhz Risc
>> ist schneller als das beste Flash
>
> Die haben trotz 160MHz weder Cache noch ausreichend breiten Prefetch?

die haben Cache  aber das Laden der nächsten Cacheline kann länger 
dauern als das ausführen der Aktuellen.
Hängt vom Code, Anfang in der Cacheline, Buss/Crossbar Arbitrierung... 
ab

und neueste gehen bis 300MHz


Wenn die CPU da sinnvolle Schleifen dreht entspannt das die Situation 
deutlich.

von Wilhelm M. (wimalopaan)


Lesenswert?

Guardians of the memory space schrieb:
> Oliver S. schrieb:
>> Von AVR ist beim TO niemals die Rede.
>
> Aber bei dem einzigen belastbaren Compiler-Output in diesem Thread und
> der TO schließt AVR nicht explizit aus -> die (einzige?)
> Optimierungsmöglichkeit in diesem Code liegt in der richtigen type
> Auswahl. Die falsche Auswahl des Types kann auch nicht durch
> Compilerschalter behoben werden.

Da hast Du einerseits recht.

Andererseits sollte man den Typ der Variablen in der for-loop 
ausreichend groß, aber eben möglichst klein wählen, d.h. man muss ihn 
abhängig von der hier zur Compilezeit bekannten Iterationsanzahl machen 
(kann man in C++ leicht mit einer Meta-Funktion). Ist das nicht zur 
Compilezeit bekannt, muss man size_t (auf AVR uint16_t) nehmen.

von Guardians of the memory space (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Andererseits sollte man den Typ der Variablen in der for-loop
> ausreichend groß, aber eben möglichst klein wählen, d.h. man muss ihn
> abhängig von der hier zur Compilezeit bekannten Iterationsanzahl machen
> (kann man in C++ leicht mit einer Meta-Funktion). Ist das nicht zur
> Compilezeit bekannt, muss man size_t (auf AVR uint16_t) nehmen.

Hast ein Beispiel für eine schleife bei der der range des schleifenindex 
aka max. Iterationsanzahl nicht zur Compilezeit bekannt ist?!

von (prx) A. K. (prx)


Lesenswert?

Volle schrieb:
> Wenn die CPU da sinnvolle Schleifen dreht entspannt das die Situation
> deutlich.

Eine CPU, die nicht in der Lage ist, sequentiell ungebremst aus dem 
CPU-Cache Code zu ziehen, erscheint mir eher exotisch. Üblicherweise 
sitzt vorne in der Pipeline ein Prefetch, der genau dafür da ist.

Demgegenüber sind manche Prefetcher recht limitiert, was das Alignment 
angeht. Was bei nichtsequentiellem Code spürbar werden kann, bei 
sequentiellem Code hingegen keine Rolle spielt.

> Hängt vom Code, Anfang in der Cacheline, Buss/Crossbar Arbitrierung...
> ab

Wenn der Zugriff auf den Cache über Bus/Crossbar geht, dann wird es sich 
wohl um einen Flash-Cache handeln, nicht um einen CPU-Cache.

L1 CPU-Caches sind üblicherweise Teil des Cores oder direkt dran, nicht 
per Bus/Crossbar. Weshalb es dafür auch egal ist, wieviele Cores es 
sind.

> und neueste gehen bis 300MHz

300 MHz ohne CPU-Cache ist schon mehr als grenzwertig.

NB: Gibts einen Link zu diesem Unikum?

von Vn N. (wefwef_s)


Lesenswert?

Guardians of the memory space schrieb:
> Aber bei dem einzigen belastbaren Compiler-Output in diesem Thread und
> der TO schließt AVR nicht explizit aus

Womit wir dann beim zweiten Punkt wären:

vn n. schrieb:
> Zweitens: mein Posting nochmal lesen, ich hab geschrieben, dass man es
> Allgemein nicht sagen kann, für konkrete Architekturen allerdings
> stimmen kann.

vn n. schrieb:
> Kann man so allgemein nicht sagen, je nach Architektur kann int auch
> schneller sein (viele 32 Bit Architekturen).

von Oliver S. (oliverso)


Lesenswert?

Markus F. schrieb:
> Oliver S. schrieb:
>> C99 ist immerhin schon aus dem letzten Jahrtausend, da kann man das
>> schon mal voraussetzen.
>>
>> Oliver
>
> Schon mal bei Microsoft über'n Zaun geguckt? Mein Rat: laß' es ;)

Na ja, man muß ja nicht das Negativbeispiel schlechthin bemühen. Wobei 
Microsoft ja wenigstens so ehrlich ist, zuzugeben, daß sie ihr "C"- 
Compiler überhaupt nicht mehr interessiert.

Oliver

von Markus F. (mfro)


Lesenswert?

Oliver S. schrieb:
> Na ja, man muß ja nicht das Negativbeispiel schlechthin bemühen.

Zu irgendwas müssen die ja gut sein ;)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Guardians of the memory space schrieb:
> Hast ein Beispiel für eine schleife bei der der range des schleifenindex
> aka max. Iterationsanzahl nicht zur Compilezeit bekannt ist?!

Dafür gibt es tausende Beispiele. Sobald einem Programm per Benutzer- 
oder Geräteschnittstelle Daten übergeben werden, kann es vorkommen.
1
Gerät: Achtung, ich übertrage jetzt 257 Datensätze...
2
µC: Ok
3
Gerät: Datensatz 1
4
µC: Ok
5
Gerät: Datensatz 2
6
µC: Ok
7
....
8
Gerät: Datensatz 256
9
µC: Urgs..
10
Gerät: Datensatz 257
11
µC: Booting...

Oder nimm ein Kassenprogramm im Laden. Du stehst mit 260 Hosen bei H&M 
an der Kasse und diese benutzt einen 8Bit-Schleifenzähler, um die Summe 
zu berechnen... Okay, das war wegen "H&M" ein schlechtes Beispiel. ;-)

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Guardians of the memory space schrieb:
> Wilhelm M. schrieb:
>> Andererseits sollte man den Typ der Variablen in der for-loop
>> ausreichend groß, aber eben möglichst klein wählen, d.h. man muss ihn
>> abhängig von der hier zur Compilezeit bekannten Iterationsanzahl machen
>> (kann man in C++ leicht mit einer Meta-Funktion). Ist das nicht zur
>> Compilezeit bekannt, muss man size_t (auf AVR uint16_t) nehmen.
>
> Hast ein Beispiel für eine schleife bei der der range des schleifenindex
> aka max. Iterationsanzahl nicht zur Compilezeit bekannt ist?!

Etwa so:
1
uint32_t sum(const uint16_t* data, uint16_t size) {
2
    uint32_t s = 0;
3
    for(uint16_t i = 0; i < size; ++i) {
4
        s += data[i];
5
    }
6
    return s;
7
}

von Stefan K. (stefan64)


Lesenswert?

Frank M. schrieb:
> Oder nimm ein Kassenprogramm im Laden. Du stehst mit 260 Hosen bei H&M
> an der Kasse und diese benutzt einen 8Bit-Schleifenzähler, um die Summe
> zu berechnen...

... oder wenn Du nur mit 128 Hosen da stehst und Dir neben den Hosen 
auch noch der komplette Kasseninhalt überreicht wird ...


Viele Grüße, Stefan

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Etwa so:

Sich diese Schleife mal auf dem gcc(amd64) mit
  gcc -std=c99 -march=corei7-avx -O3
anzusehen kann recht illustrativ sein.

: Bearbeitet durch User
von Guardians of the memory space (Gast)


Lesenswert?

vn n. schrieb:
> Guardians of the memory space schrieb:
>> Aber bei dem einzigen belastbaren Compiler-Output in diesem Thread und
>> der TO schließt AVR nicht explizit aus
>
> Womit wir dann beim zweiten Punkt wären:
>
> vn n. schrieb:
>> Zweitens: mein Posting nochmal lesen, ich hab geschrieben, dass man es
>> Allgemein nicht sagen kann, für konkrete Architekturen allerdings
>> stimmen kann.
>
> vn n. schrieb:
>> Kann man so allgemein nicht sagen, je nach Architektur kann int auch
>> schneller sein (viele 32 Bit Architekturen).

Hm, also ich mein nicht allgemein sondern ganz konkret den in diesem 
posting
Beitrag "Re: sei nicht schlauer als der Compiler?"
geschilderten Fall, das sich auf das posting bezieht:
Beitrag "Re: sei nicht schlauer als der Compiler?"

welches den m.E. falschen Schluss zieht das der beispielcode in beiden 
Fällen optimal für den Compiler wäre. Optimal wird der Code aber erst 
wenn der Wertebereich der Variable "passend" ist - da sind wir doch 
gleicher Meinung?! - Und um diese Angabe gegenüber dem Compiler machen 
zu können, muß der User schlauer sein als der Compiler.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Adib schrieb:
> es gibt ja so die Regeln beim Optimieren, dass man nicht schlauer sein
> sollte als der Compiler und der Compiler schon so optimiert.

Noch viel wichtiger zu beherzigen ist, dass man nicht dümmer sein 
sollte als der Compiler.

von Wilhelm M. (wimalopaan)


Lesenswert?

Guardians of the memory space schrieb:
> vn n. schrieb:
>> Guardians of the memory space schrieb:
>>> Aber bei dem einzigen belastbaren Compiler-Output in diesem Thread und
>>> der TO schließt AVR nicht explizit aus
>>
>> Womit wir dann beim zweiten Punkt wären:
>>
>> vn n. schrieb:
>>> Zweitens: mein Posting nochmal lesen, ich hab geschrieben, dass man es
>>> Allgemein nicht sagen kann, für konkrete Architekturen allerdings
>>> stimmen kann.
>>
>> vn n. schrieb:
>>> Kann man so allgemein nicht sagen, je nach Architektur kann int auch
>>> schneller sein (viele 32 Bit Architekturen).
>
> Hm, also ich mein nicht allgemein sondern ganz konkret den in diesem
> posting
> Beitrag "Re: sei nicht schlauer als der Compiler?"
> geschilderten Fall, das sich auf das posting bezieht:
> Beitrag "Re: sei nicht schlauer als der Compiler?"
>
> welches den m.E. falschen Schluss zieht das der beispielcode in beiden
> Fällen optimal für den Compiler wäre. Optimal wird der Code aber erst
> wenn der Wertebereich der Variable "passend" ist - da sind wir doch
> gleicher Meinung?! - Und um diese Angabe gegenüber dem Compiler machen
> zu können, muß der User schlauer sein als der Compiler.

Nein.

1) In C könnte man das durch den Präprozessor erledigen.

2) In C++:
1
constexpr auto size = 142;
2
3
using type = minimumTypeFor<size>::type;
4
5
for(type i = 0; i < size; ++i) {
6
    //...
7
}

von Guardians of the memory space (Gast)


Lesenswert?

Johann L. schrieb:

> Noch viel wichtiger zu beherzigen ist, dass man nicht dümmer sein
> sollte als der Compiler.

Wobei es nach Linus Torvalds nix dümmeres als den gcc gibt:
"For chrissake, that
 compiler shouldn't have been allowed to graduate from kindergarten.
 We're talking "sloth that was dropped on the head as a baby" level
 retardation levels here"


https://www.heise.de/developer/meldung/Linus-Torvalds-wettert-gegen-Compiler-Collection-GCC-4-9-2268920.html

von (prx) A. K. (prx)


Lesenswert?

Wobei man nicht jeden Spruch von ihm auf die Goldwaage legen sollte.

von Guardians of the memory space (Gast)


Lesenswert?

Wilhelm M. schrieb:
>>
>> welches den m.E. falschen Schluss zieht das der beispielcode in beiden
>> Fällen optimal für den Compiler wäre. Optimal wird der Code aber erst
>> wenn der Wertebereich der Variable "passend" ist - da sind wir doch
>> gleicher Meinung?! - Und um diese Angabe gegenüber dem Compiler machen
>> zu können, muß der User schlauer sein als der Compiler.
>
> Nein.
>
> 1) In C könnte man das durch den Präprozessor erledigen.

Ja, dann ist eben der von User genutzte C-Präprozosser schlauer als der 
Compiler - läuft auf das selbe hinaus - der Compiler ist nicht schlauer 
als sein Input.

von Guardians of the memory space (Gast)


Lesenswert?

A. K. schrieb:
> Wobei man nicht jeden Spruch von ihm auf die Goldwaage legen sollte.

Aber wo Linus recht hat, hat er recht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Guardians of the memory space schrieb:
> Johann L. schrieb:
>
>> Noch viel wichtiger zu beherzigen ist, dass man nicht dümmer sein
>> sollte als der Compiler.
>
> Wobei es nach Linus Torvalds nix dümmeres als den gcc gibt:

Soll er eben llvm nehmen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Guardians of the memory space schrieb:
> Wilhelm M. schrieb:
>>>
>>> welches den m.E. falschen Schluss zieht das der beispielcode in beiden
>>> Fällen optimal für den Compiler wäre. Optimal wird der Code aber erst
>>> wenn der Wertebereich der Variable "passend" ist - da sind wir doch
>>> gleicher Meinung?! - Und um diese Angabe gegenüber dem Compiler machen
>>> zu können, muß der User schlauer sein als der Compiler.
>>
>> Nein.
>>
>> 1) In C könnte man das durch den Präprozessor erledigen.
>
> Ja, dann ist eben der von User genutzte C-Präprozosser schlauer als der
> Compiler - läuft auf das selbe hinaus - der Compiler ist nicht schlauer
> als sein Input.

Nein.
Der Compiler macht genau das, was ich der Programmierer ihm sagt. Wenn 
da int als Typ steht, darf der Compiler nichts anderes nehmen.
Was hier in dem Beispiel eben fehlt - und das kann der Compiler nicht 
alleine - ist eine Beziehung zwischen einer Konstanten auf der einen 
Seite und einem Datentyp an anderer Stelle herstellen.

von Guardians of the memory space (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nein.
> Der Compiler macht genau das, was ich der Programmierer ihm sagt.

Ganz mein reden, dummer Input bspw 16bit wo 8bit passt -> dummer Output


> Wenn
> da int als Typ steht, darf der Compiler nichts anderes nehmen.

Ja, genau ganz mein reden - int ist da suboptimal, das kriegt kein 
Optimierer wech.

> Was hier in dem Beispiel eben fehlt - und das kann der Compiler nicht
> alleine - ist eine Beziehung zwischen einer Konstanten auf der einen
> Seite und einem Datentyp an anderer Stelle herstellen.

Hm, gibt es nicht da doch eine spezifizierte Beziehung resp welche Länge 
der Compiler bei Konstanten anzunehmen hat?
"Im allgemeinen ordnet der Compiler einer numerischen, ganzzahligen 
Konstanten den kleinstmöglichen Datentyp zu."
aus http://c-buch.sommergut.de/Kapitel3/Konstanten.shtml

Aber damit kann er eben den vom User (grösser als nötig) verlangten 
Datentyp nicht verkleinern.

Würde da ein cast bei der Konstanten Deklaration helfen oder zumindest 
eine Warning generieren?

von j67 (Gast)


Lesenswert?

> Andererseits sollte man den Typ der Variablen in der for-loop
> ausreichend groß, aber eben möglichst klein wählen, d.h. man muss ihn
> abhängig von der hier zur Compilezeit bekannten Iterationsanzahl machen
> (kann man in C++ leicht mit einer Meta-Funktion). Ist das nicht zur
> Compilezeit bekannt, muss man size_t (auf AVR uint16_t) nehmen.


Dafür gibt es doch uint8_fast_t etc.

von Wilhelm M. (wimalopaan)


Lesenswert?

Guardians of the memory space schrieb:

> Hm, gibt es nicht da doch eine spezifizierte Beziehung resp welche Länge
> der Compiler bei Konstanten anzunehmen hat?

Literale haben auch einen Typ: int, long, long, unsigned int, ...

> "Im allgemeinen ordnet der Compiler einer numerischen, ganzzahligen
> Konstanten den kleinstmöglichen Datentyp zu."
> aus http://c-buch.sommergut.de/Kapitel3/Konstanten.shtml

Vielleicht damit der Type der Literale gemeint ...


> Würde da ein cast bei der Konstanten Deklaration helfen oder zumindest
> eine Warning generieren?

In der for-loop steht immer noch int ...

von Markus F. (mfro)


Lesenswert?

Guardians of the memory space schrieb:
> Optimal wird der Code aber erst
> wenn der Wertebereich der Variable "passend" ist - da sind wir doch
> gleicher Meinung?! - Und um diese Angabe gegenüber dem Compiler machen
> zu können, muß der User schlauer sein als der Compiler.

Im Gegenteil. Wenn Du, wie Du oben vorgeschlagen hast, tatsächlich mal 
die Schleife mit einem 8 Bit breiten Schleifenzähler compilierst, wirst 
Du feststellen, daß die meisten Compiler hier schlauer sind als die 
(zumindest viele) User.

Ein nur mittelmäßig schlauer Compiler wird nämlich trotzdem mit einem 
int rechnen, weil er weiß, daß er für die Addition integer promotion 
machen muß.

Und ein noch schlauerer Compiler wird wissen, daß dabei dasselbe 
rauskommt, wie wenn er gleich mit int8_t gerechnet hätte und die integer 
promotion wieder wegoptimieren.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Der Compiler macht genau das, was ich der Programmierer ihm sagt. Wenn
> da int als Typ steht, darf der Compiler nichts anderes nehmen.

Wenn der Compiler weiss, dass keine Werte ausserhalb beispielsweise 
0..10 vorkommen können, dann kann er die Schleife nach belieben 
optimieren, egal mit welcher internen Darstellung. So lange er sich so 
verhält, als ob es "int" sei.

Wenn der Compiler sich beispielsweise entschliessen sollte die 10 
Iterationen entrollt direkt hintereinader zu schreiben, dann kann die 
Variable auch völlig entfallen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Der Compiler macht genau das, was ich der Programmierer ihm sagt. Wenn
>> da int als Typ steht, darf der Compiler nichts anderes nehmen.
>
> Wenn der Compiler weiss, dass keine Werte ausserhalb beispielsweise
> 0..10 vorkommen können, dann kann er die Schleife nach belieben
> optimieren, egal mit welcher internen Grösse. Er muss sich nur so
> verhalten, als ob es "int" wäre.

Genau, die as-if-rule.

Das weiß ich also nicht, ob oder ob nicht.
Der avr-gcc machts in diesem Beispiel jedenfalls nicht. Dann kann ich 
aber helfen, so wie ich es beschrieben habe.
So etwas macht man dann bespielsweise auch in std::array<>, dort habe 
ich einen
geschachtelten Typ, den ich dann nehmen kann. Dann passt es immer(!), 
egal ob mit oder ohne Optimierung nach as-if-rule.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Der avr-gcc machts in diesem Beispiel jedenfalls nicht.

In der Sprache ist "int" als ein Basistyp der Zielmaschine definiert, 
also als Wortbreite dieser Maschine. Allgemeine Optimierungen des 
Compilers dürfen also davon ausgehen, dass das ein Typ ist, mit dem die 
Zielmaschine perfekt umgehen kann.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Der avr-gcc machts in diesem Beispiel jedenfalls nicht.
>
> In der Sprache ist "int" als ein Basistyp der Zielmaschine definiert,
> also als Wortbreite dieser Maschine. Allgemeine Optimierungen des
> Compilers dürfen also davon ausgehen, dass das ein Typ ist, mit dem die
> Zielmaschine perfekt umgehen kann.

Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer, 
vorzeichenbehafteter Typ ist. Und das hat gar nichts mit der 
Ziel-Architektur zu tun!!!

von Christian S. (christian_s593)


Lesenswert?

Guardians of the memory space schrieb:
>> Wenn
>> da int als Typ steht, darf der Compiler nichts anderes nehmen.
>
> Ja, genau ganz mein reden - int ist da suboptimal, das kriegt kein
> Optimierer wech.

Nicht ganz richtig, ab den Optimierlevel -O3 wird die ganze Schleife 
ausgerollt und es existiert dann keine Zählvariable mehr.

Zu mein oben genannten Beispiel hab ich auch extra eine volatile 
Variable "i" in der Schleife gepackt, ab Level -O2 geht der Compiler 
nämlich bei und schreibt direkt den Zielwert in "i".

Und ja, wenn der Programmierer nicht weiß was er macht, ist es immer 
schlecht. Aber auch das "Problem" mit den 16 bit Zählvariable ist 
eigentlich keins. In den oberen Beispiel verliert man ein Zyklus bei dem 
AVR, also im schlimmsten Fall 256 Takte, darüber braucht man sowieso ein 
int. Wenn es auf diese paar Takte wirklich ankommt, nimmt man halt -O3 
oder man guckt sich die betreffende Stelle genauer an.

Was ein schönes Feature wäre, wenn man betreffende Stellen mit -O3 und 
andere mit -Os optimieren könnte, also quasi eine For-Schleife in einer 
ISR mit -O3 markieren könnte, weil niemand will per Hand eine Schleife 
ausrollen, macht die Wartbarkeit ja auch kaputt.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
> vorzeichenbehafteter Typ ist. Und das hat gar nichts mit der
> Ziel-Architektur zu tun!!!

Mag sein, dass du das nicht weisst. Ein Compiler wie GCC ist aber 
schlauer, denn er weiss dank Analyse des Codes, dass obige Schleife 
keine Werte ausserhalb 0..10 annehmen kann. Vorausgesetzt es sind keine 
weiteren Referenzen auf "i" vorhanden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Christian S. schrieb:

> Was ein schönes Feature wäre, wenn man betreffende Stellen mit -O3 und
> andere mit -Os optimieren könnte, also quasi eine For-Schleife in einer
> ISR mit -O3 markieren könnte, weil niemand will per Hand eine Schleife
> ausrollen, macht die Wartbarkeit ja auch kaputt.

Stimmt einerseits in diesem Minimalbeispiel.
Andereseits kann einem -O3 die totale Codegröße ziemlich anwachsen 
lassen, was man ggf. auch nicht möchte.

von (prx) A. K. (prx)


Lesenswert?

Christian S. schrieb:
> Was ein schönes Feature wäre, wenn man betreffende Stellen mit -O3 und
> andere mit -Os optimieren könnte,

#pragma GCC push_options
#pragma GCC optimize ("O3")

...

#pragma GCC pop_options

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
>> vorzeichenbehafteter Typ ist. Und das hat gar nichts mit der
>> Ziel-Architektur zu tun!!!
>
> Mag sein, dass du das nicht weisst. Ein Compiler wie GCC ist aber
> schlauer, denn er weiss dank Analyse des Codes, dass obige Schleife
> keine Werte ausserhalb 0..10 annehmen kann. Vorausgesetzt es sind keine
> weiteren Referenzen auf "i" vorhanden.

Nicht jeder, s.o.
Zumindest der avr-gcc macht es nicht. Der macht brav sbiw statt subi.

Und das Problem der nicht angepassten Datentypen beschränkt sich ja 
nicht nur auf dieses kleine Beispiel.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:
> #pragma GCC optimize ("O3")

Alternativ
int __attribute__((optimize("O3")))
function(...) {
 ...
}

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Zumindest der avr-gcc macht es nicht.

Doch. Er berücksichtig es aber nicht an jeder Stelle, an der du es gerne 
hättest.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Zumindest der avr-gcc macht es nicht.
>
> Doch. Er berücksichtig es aber nicht an jeder Stelle, an der du es gerne
> hättest.

Es geht doch hier um das Eingangsbeispiel!

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
>> Doch. Er berücksichtig es aber nicht an jeder Stelle, an der du es gerne
>> hättest.
>
> Es geht doch hier um das Eingangsbeispiel!

Ja und? GCC führt definitiv eine Wertebereichsanalyse durch. Nur führt 
die eben nicht dazu, dass er deshalb für die Variable eine 
Registerbreite verwendet, die für AVR optimal ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>>> Doch. Er berücksichtig es aber nicht an jeder Stelle, an der du es gerne
>>> hättest.
>>
>> Es geht doch hier um das Eingangsbeispiel!
>
> Ja und? GCC führt definitiv eine Wertebereichsanalyse durch. Nur führt
> die eben nicht dazu, dass er deshalb für die Variable eine
> Registerbreite verwendet, die für AVR optimal ist.

Du willst es nicht verstehen, oder?

Genau das habe ich doch die ganze Zeit gesagt. Er optimiert nicht zu 
8-Bit.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Du willst es nicht verstehen, oder?

Ich bezog mich auf diese Aussage:

Wilhelm M. schrieb:
> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
> vorzeichenbehafteter Typ ist.

Und das ist schlicht falsch, wenn als "ich" der Compiler gemeint ist. Er 
weiss mehr, nutzt dieses Wissen aber nicht so, wie du es gerne hättest.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Du willst es nicht verstehen, oder?
>
> Ich bezog mich auf diese Aussage:
>
> Wilhelm M. schrieb:
>> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
>> vorzeichenbehafteter Typ ist.
>
> Und das ist schlicht falsch, wenn als "ich" der Compiler gemeint ist. Er
> weiss mehr, nutzt dieses Wissen aber nicht so, wie du es gerne hättest.

Wieso sollte ich so schizophren sein? Ich bin ich und nicht der Compiler 
;D

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
> vorzeichenbehafteter Typ ist. Und das hat gar nichts mit der
> Ziel-Architektur zu tun!!!

A.K. hat schon recht. Ein int ist schon seit den 70ern so definiert, 
dass es die "natürliche Wortbreite" des Ziel-Prozessors abdeckt. Dass 
der avr-gcc 16-Bit für einen int nimmt, ist wohl eher praktischen 
Gründen geschuldet, denn mit einem 8-Bit int kann man herzlich wenig 
anfangen - ganz zu schweigen von zu erwartenden Inkompatiblitäten mit 
libc-Funktionen. Kann aber auch sein, dass später die Forderung 
"mindestens 16 Bit für ein int" dazugekommen ist.

Übrigens: Es gibt durchaus C-Compiler für AVR, die mit einem 8-Bit int 
arbeiten.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
>> vorzeichenbehafteter Typ ist. Und das hat gar nichts mit der
>> Ziel-Architektur zu tun!!!
>
> A.K. hat schon recht. Ein int ist schon seit den 70ern so definiert,
> dass es die "natürliche Wortbreite" des Ziel-Prozessors abdeckt. Dass
> der avr-gcc 16-Bit für einen int nimmt, ist wohl eher praktischen
> Gründen geschuldet, denn mit einem 8-Bit int kann man herzlich wenig
> anfangen - ganz zu schweigen von zu erwartenden Inkompatiblitäten mit
> libc-Funktionen. Kann aber auch sein, dass später die Forderung
> "mindestens 16 Bit für ein int" dazugekommen ist.

Wann was historisch gekommen ist, ist doch hier erst mal egal:

Das gilt:

http://en.cppreference.com/w/c/language/arithmetic_types

Und da steht es so, wie ich es gesagt habe.

>
> Übrigens: Es gibt durchaus C-Compiler für AVR, die mit einem 8-Bit int
> arbeiten.

Das kann ich beim (avr-)gcc auch ändern, wenn ich will. Nur dann ist es 
nicht mehr dem Standard entsprechend.

von Nop (Gast)


Lesenswert?

Frank M. schrieb:

> A.K. hat schon recht. Ein int ist schon seit den 70ern so definiert,
> dass es die "natürliche Wortbreite" des Ziel-Prozessors abdeckt.

Der C-Standard definiert int mit mindestens 16 bit. Auf einem 
32bit-System wird das üblicherweise 32bit haben, garantiert ist es aber 
nicht. Man darf sogar auf einem 64bit-System int mit 16 bit 
implementieren.

Nur darf man auf einem System mit 8 bit Wortbreite int eben nicht mit 8 
bit implementieren.

von (prx) A. K. (prx)


Lesenswert?

Frank M. schrieb:
> Dass der avr-gcc 16-Bit für einen int nimmt, ist wohl eher praktischen
> Gründen geschuldet,

Nö. Sondern weil man -32767..32767 nicht in ein Byte quetschen kann. Das 
ist nämlich der Mindeswertebereich "int", aka MIN_INT..MAX_INT in 
stdint.h:
http://www.cplusplus.com/reference/climits/

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Wenn da int als Typ steht, darf der Compiler nichts anderes nehmen.

Doch natürlich darf er.  Solange sich die abstrakte Maschine genauso 
verhält wie mit int.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Wenn da int als Typ steht, darf der Compiler nichts anderes nehmen.
>
> Doch natürlich darf er.  Solange sich die abstrakte Maschine genauso
> verhält wie mit int.

Ok, schlecht ausgedrückt. Die as-if Regel hatte ich obrn schon. Aber 
auch das ist doch gar nicht der Punkt hier ...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Dass der avr-gcc 16-Bit für einen int nimmt, ist wohl eher praktischen
> Gründen geschuldet, denn mit einem 8-Bit int kann man herzlich wenig
> anfangen - ganz zu schweigen von zu erwartenden Inkompatiblitäten mit
> libc-Funktionen. Kann aber auch sein, dass später die Forderung
> "mindestens 16 Bit für ein int" dazugekommen ist.

Wie bereits angemerkt schreibt der Standard mindestens 16 Bits vor.

> Übrigens: Es gibt durchaus C-Compiler für AVR, die mit einem 8-Bit int
> arbeiten.

Dann sind sie aber noch weniger Standard-konform als avr-gcc (bei dem 
nur double zu klein für den Standard ist).

Außerdem kennt avr-gcc -mint8 mit dem ein int dann 8 Bits hat (inclusive 
entsprechender Promotion Rules) und ein long 16 Bit.  Aber -mint8 ist 
keine Multilib-Option, d.h. man sollte tunlichst -nodefaultlibs 
-nostdlib verwenden und alle unaufgelösten Symbole selber 
implementieren.

: Bearbeitet durch User
von Guardians of the memory space (Gast)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> Nein, alles was ich weiß ist, dass ein int ein mindestens 16-Bit großer,
>> vorzeichenbehafteter Typ ist. Und das hat gar nichts mit der
>> Ziel-Architektur zu tun!!!
>
> A.K. hat schon recht. Ein int ist schon seit den 70ern so definiert,
> dass es die "natürliche Wortbreite" des Ziel-Prozessors abdeckt.

Wobei sich die Frage auftut, ob die C-Väter jemals geplant haben für 8 
bit CPU's C zu verwenden.
Nach meiner Erinnerungen erschienen die C-Compiler für die 8bit 
Controller erst reichlich spät. Brian W. Kernighan und Dennis Ritchie 
haben damals C ausgeheckt um Unix auf der PDP-11 (16bit-Maschine) zu 
programmieren. Somit kann ich mir gut vorstellen das bei "natürliche 
Wortbreite" in C nie an 8bit gedacht wurde. 8bit gelten da eben nicht 
als Breite für "(data/Instruction-)Wort" sondern als (druckbares) 
"Zeichen" im Sinne von ASCII-Code für eine 
teletypwriter(tty)/UART-Bediener-Console.

Und die ersten C-Compiler für die kleinen µC taten sich lange schwer 
brauchbare Ergebnisse abzuliefern.
Und gelegentlich taucht die Meinung auf, das man eine 8bit 
Architektur/Befehlssatz extra darauf auslegen muss um sie effizient in C 
programmieren zu können.

von (prx) A. K. (prx)


Lesenswert?

Guardians of the memory space schrieb:
> Wobei sich die Frage auftut, ob die C-Väter jemals geplant haben für 8
> bit CPU's C zu verwenden.

Nein. Es gab nämlich keine als C anfing, jedenfalls keine 
Mikroprozessoren. Es gab beispielsweise 12-Bit Minicomputer, aber die 
hatten sie auch nicht auf dem Radar.

> Nach meiner Erinnerungen erschienen die C-Compiler für die 8bit
> Controller erst reichlich spät.

In den 80ern kamen Schmalspur-Compiler, Small-C, Tiny-C und wie das Zeug 
auch immer hiess. Vielleicht gab es auch echtes C, aber C war Anfang der 
80er noch nicht die dominante Sprache. Dafür gab es Fortran, PL/I und 
Cobol Compiler für CP/M (eine recht eindrucksvolle Leistung).

C Compiler für 8 Bit wurden wirtschaftlich erst interessant, als man C 
nicht nur für PCs, sondern auch für Mikrocontroller verwenden wollte.

> Architektur/Befehlssatz extra darauf auslegen muss um sie effizient in C
> programmieren zu können.

Wenn ein Compiler von inneren Aufbau her auf register-orientierte 
Maschinen ausgerichtet ist, dann tut man sich schwer damit, ihn auf 
Akku-Architekturen zu portieren. Es gibt (oder gab) im GCC eine 
Zielmaschinenspezifikation für die besseren 68HC1x Akku-Maschinen. Aber 
wenn man da reinsieht, dann findet man Pseudoregister im RAM. Auch bei 
den Renesas R8c/M16c fand ich so etwas, anscheinend sind deren paar 
Register zu wenig.

Ein ähnliches Problem stellte sich mir bei einer Art Vorläufer des GCC, 
dem PCC, bei einer Portierung auf Transputer. Auch der PCC setzte eine 
gewisse Zahl Register voraus, der Transputer hat aber nur einen Stack. 
Das endete mit einem vollständig neu geschriebenen Codegenerator.

Aber auch völlig unabhängig von den Compilern profitiert C von 
bestimmten Eigenschaften einer Maschine, die in Assembler auf kleinen 
Mikrocontrollern nicht ganz so wichtig sind.

Dazu zählt die Adressierbarkeit von Daten relativ zu Adressregistern 
(*). Das ist in C viel wichtiger als in Assembler oder Fortran. Etliche 
8-Bit CPUs tun sich damit etwas schwer. Auch AVR-Code sieht man an, das 
es zu wenig Pointer-Register gibt. Gute Compiler für 8051 adressieren 
deshalb lokale "auto" Daten statisch und treiben gewisse Blüten um 
Funktionen ggf. reentrant zu bekommen.

Ein weiterer Punkt ist das Vorzeichen. Manchen 8-Bittern fehlt die 
Möglichkeit, direkt auf Basis vorzeichenbehafteter Typen zu springen. 
Sei es, weil ihnen das dafür üblicherweise verwendete Overflow-Flag 
fehlt, sei es, weil ihnen die Befehle dazu fehlen. Aufgrund der 
Promotion-Regeln hat man ein Vorzeichen aber häufiger im Boot, als einem 
lieb ist.

Keine gute Idee ist Bank-Switching im RAM. Der erste Entwurf von AVRs 
hatte das wohl auch drin, aber ein Compiler-Hersteller kriegte die Krise 
und setzte sich durch.

*: Als Zilog die 16-Bit Z8000 Konkurrenz zu 8086 und 68000 rausbrachte, 
hatten sie erkennbar eher Fortran als C im Auge. Die kann das zwar, 
kommt aber mit *(addresse+indexregister) und *addressregister deutlich 
besser klar als mit *(addressregister+offset).

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Johann L. schrieb:
> Guardians of the memory space schrieb:
>> Johann L. schrieb:
>>
>>> Noch viel wichtiger zu beherzigen ist, dass man nicht dümmer sein
>>> sollte als der Compiler.
>>
>> Wobei es nach Linus Torvalds nix dümmeres als den gcc gibt:
>
> Soll er eben llvm nehmen.

Oder sich mal überlegen wo er ohne den GCC heute wäre.

von Mikro 7. (mikro77)


Lesenswert?

Linus schimpft ja gerne. Und, wenn ich mich richtig erinnere, hatte er 
auch gute Gründe. Aber es scheint dennoch irgendwie zu gehen. Ansonsten 
hätte er schon seinen eigenen Compiler geschrieben. :-) /OT

: Bearbeitet durch User
von Berufsrevolutionär (Gast)


Lesenswert?

> Berufsrevolutionär schrieb
[AVR C/ASM-Codebeispiel>
Naja hier ist der Compiler genauso unsmart wie der User.
vn n. schrieb:

> Berufsrevolutionär schrieb:
>> Integer ist der falsche Typ (16 bit) für den Schleifenindex
>> (Wertebereich 10 bis 0), dadurch wird der Code langsamer und größer als
>> nötig.
>> char ist die bessere Wahl. auch hinsichtlich der höheren Zahl von
>> Speicherstellen für 16 bit Werte.
>
> Kann man so allgemein nicht sagen, je nach Architektur kann int auch
> schneller sein (viele 32 Bit Architekturen).
> Allerdings sollten Schleifenzähler grundsätzlich unsigned sein, wenn
> nämlich die Anzahl der durchläufe zur Compilezeit nicht bekannt ist, tut
> sich der Compiler mit unsigned xyz nämlich wesentlich leichter bei der
> Optimierung.

> char ist für Zählvariablen sowieso ungeeignet, da im Standard nicht
> definiert ist, ob signed oder unsigned
> (http://www.trilithium.com/johan/2005/01/char-types/).

> Die richtige Wahl wäre uint_fast8_t: dieser ist definiert als
> schnellster unsigned Datentyp mit mindestens 8 Bit Breite.
> Will man (aus welchen Gründen auch immer) stdint.h nicht nutzen, wäre
> unsigned char Mittel der Wahl.

Danke für die Ausführungen.
Ich programmierbar halt meist µC  maschinennah und finde da die zuweilen 
ausufernde Typenvielfalt in C mehr verwirrend als hilfreich. Viele 
Typbezeichner sind redundant oder die wortwörtliche Übersetzung hilft 
nicht weiter da es die suggerierte Unterscheidung in der jeweiligen 
Architektur nicht gibt.
Beispielsweise bringt ein AVR eben Befehle für 8 und 16 bit Daten bit, 
Unterscheidung in vorzeichen-typen/Operatoren gibt es garnicht(? oder 
ist verzichtbar!).
Also alles im Wortsinne Integer und jeweils normal (8bit) und doppelt 
(16bit) breit - wird dann aber in C als "unsigned char" und "integer" 
(oder unsigned integer ?) bezeichnet.  Und jetzt noch die Unterscheidung 
in schnelle und nicht-schnelle Typen?

Ich bin der Meinung, das man den jeweiligen Zielprozessor soweit kennen 
sollte, das man die Nach/Vorteile des Benutzung der jeweiligen 
Operandenlänge beachtet.
Und dann sollte man eben nur die Bezeichner für Maschinen-Grundtypen 
verwenden und nicht irgendwelche aus "Unsigned" "long" 
zusammnengefrickelte Synonyme.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Berufsrevolutionär schrieb:
> Beispielsweise bringt ein AVR eben Befehle für 8 und 16 bit Daten bit,
> Unterscheidung in vorzeichen-typen/Operatoren gibt es garnicht(? oder
> ist verzichtbar!).

Doch, es gibt die Unterscheidung zwischen vorzeichenbehafteten und
-losen Zahlen auch beim AVR, siehe MUL, FMUL, MULS, FMULS, MULSU,
FMULSU, LSR¹, ASR¹, NEG, BRLO, BRSH, BRLT und BRGE.

————————————
¹) als Division durch 2 betrachtet

von (prx) A. K. (prx)


Lesenswert?

Berufsrevolutionär schrieb:
> Beispielsweise bringt ein AVR eben Befehle für 8 und 16 bit Daten bit,
> Unterscheidung in vorzeichen-typen/Operatoren gibt es garnicht(? oder
> ist verzichtbar!).

Entweder unterscheiden Vergleichsbefehle zwischen vorzeichenlos und 
vorzeichenbehaftet (z.B. MIPS), oder Condition Codes enthalten die 
nötige Information und die bedingten Sprünge werten sie entsprechend 
aus. 8051, 6502, 8080/Z80 und PIC fehlen teils die Flags und allen die 
Sprungbedingungen, und sie können deshalb mit vorzeichenbehafteten 
Vergleichen deutlich schlechter umgehen.

Vergleichsbefehle lassen sich bei AVRs zudem so kaskadieren, dass das 
Ergebnis auch bei Multibyte-Typen passt (dank CPC und der Handhabung des 
Z-Flags). Das kenne ich davor nur vom Z8.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Berufsrevolutionär schrieb:
> Ich programmierbar halt meist µC  maschinennah und finde da die zuweilen
> ausufernde Typenvielfalt in C mehr verwirrend als hilfreich.

Eine universelle Programmiersprache kommt kaum darum herum, sowohl für 
die verschiedenen Maschinentypen als auch die üblicherweise von 
Anwendungen geforderten Typen eine Hochsprachendarstellung zu 
ermöglichen. Die Frage ist nur wie. C verwendet dazu die bekannte 
Kombination von Schlüsselwörtern, deren Bedeutung in der Implementierung 
festgelegt wird. In PL/I schreibt man die Länge schlicht rein: dcl i 
binary(7) für eine 8-Bit Variable mit Vorzeichen, vorzeichenlos gab es 
m.W. nicht.

> Viele Typbezeichner sind redundant

Wenn man wie in C Bezeichner zur Unterscheidung der Typen verwendet, 
dann ist Redundanz schlecht vermeidbar. Es gibt zu viele Variationen von 
Maschinen. Wenn eine 32-Bit Maschine keine 16-Bit Datentypen kennt 
(32-Bit Transputer), dann sind short=int=long=32. Im LP64 Modell der 
64-Bit x86 hingegen besteht keine Redundanz: short=16, int=32, long=64.

> Ich bin der Meinung, das man den jeweiligen Zielprozessor soweit kennen
> sollte, das man die Nach/Vorteile des Benutzung der jeweiligen
> Operandenlänge beachtet.

Manche meiner Programme und Libraries gehen zurück auf die 80er Jahre 
und haben 32bit 68000, 16bit x86, 32bit x86, 64bit x86 und IBM POWER 
erlebt. Spätere haben AVR und ARM erlebt. Da lohnt es sich, die 
Datentypen nicht auf direkt auf eine einzelne Zielmaschine zu 
optimierten, sondern sich über deren Variationen Gedanken zu machen. 
Damit man aufgrund verschiedener Interpretationen von z.B. "int" nicht 
für jede Portierung die Typen anpassen muss und trotzdem effizient 
bleibt.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Ich habe es noch nie erlebt, wenn man Code vom AVR auf den ARM portiert, 
daß dann ein uint8_t plötzlich zum Flaschenhals wird.
Die ARM haben eh massig Flash, da fällt ein überzähliges x&=0xFF; 
überhaupt nicht auf.
Die xx_fast_xx Typen sind nur was für Pedanten.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Ich habe es noch nie erlebt, wenn man Code vom AVR auf den ARM
> portiert, daß dann ein uint8_t plötzlich zum Flaschenhals wird.

Das stimmt schon: es ist schwierig, einen STM32 zum Schwitzen zu 
bekommen - außer man macht es böswillig oder aus Dummheit.

Hier ein Thread, wo das diskutiert wird:

Beitrag "STM32: Arrays und Performance"

Tatsächlich ist es so, dass die Abarbeitung von Schleifen wesentlich 
schneller abläuft, wenn die Laufvariablen 32 Bit breit sind. Als 
Array-Indices sind aber demgegenüber uint8_t Variablen etwas flotter.

> Die xx_fast_xx Typen sind nur was für Pedanten.

Das sehe ich anders. Diese Typen fördern die Portabilität von Code für 
verschiedenen Zielplattformen, ohne einen Kompromiss bei der Performance 
eingehen zu müssen.

von Nop (Gast)


Lesenswert?

Frank M. schrieb:

> Das stimmt schon: es ist schwierig, einen STM32 zum Schwitzen zu
> bekommen - außer man macht es böswillig oder aus Dummheit.

Wenn man damit sowas baut, kommt der definitiv ins Schwitzen:

http://www.millennium2000.de/chessgenius-pro

(Ist jetzt nicht exakt STM32, aber ein ähnlicher Cortex-M4.)

von Guardians of the memory space (Gast)


Lesenswert?

Peter D. schrieb:
> Ich habe es noch nie erlebt, wenn man Code vom AVR auf den ARM portiert,
> daß dann ein uint8_t plötzlich zum Flaschenhals wird.
> Die ARM haben eh massig Flash, da fällt ein überzähliges x&=0xFF;
> überhaupt nicht auf.

Also IMHO sollte der Compiler durch ne Speed-Optimierungsoption  dazu 
gebracht werden eine uint8_t - Variable automatisch durch eine 
schnellere ersetzen können, auch wenn diese breiter als nötig ist. Und 
wenns schaltet man eben die Spoeed-Optimierung für diesen Codeteil 
wieder aus.


> Die xx_fast_xx Typen sind nur was für Pedanten.

Seit wann gibst die überhaupt in der stdint.h? C11? C99?
Scheint irgendein so ein neumodisches Pflaster für ne alte Krücke zu 
sein?

In der stdint.h für AVR finde ich jetzt 26 Typdefinitionen, plus defines 
für die Bereichsgrenzen - wer braucht das zum Glücklich sein?
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Guardians of the memory space schrieb:
> Also IMHO sollte der Compiler durch ne Speed-Optimierungsoption  dazu
> gebracht werden eine uint8_t - Variable automatisch durch eine
> schnellere ersetzen können, auch wenn diese breiter als nötig ist.

Ein Ändern des Typs durch globalen Rundumschlag halte ich für nicht 
sinnvoll. Das geht für Arrays (Größe!) oder für uint8_t-Variablen, bei 
denen der Überlauf von 255 auf 0 beabsichtigt ist, voll in die Hose. 
Außerdem beträfe so eine globale "Optimierungsoption" auch das Interface 
zur libc und anderen Bibliotheken.

Nein, das uint_fast8_t muss man schon sehr bewusst einsetzen und man 
muss wissen, was das für Konsequenzen haben kann, wie zum Beispiel 
fehlenden Überlauf. Ist also nix für Leute, die nicht nachdenken wollen.

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Guardians of the memory space schrieb:
> Also IMHO sollte der Compiler durch ne Speed-Optimierungsoption  dazu
> gebracht werden eine uint8_t - Variable automatisch durch eine
> schnellere ersetzen können, auch wenn diese breiter als nötig ist.

In realitas werden die fast-typen einfach fest auf passende Int-Typen 
getypdefed.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Guardians of the memory space schrieb:

>
> Seit wann gibst die überhaupt in der stdint.h? C11? C99?

C99

> Scheint irgendein so ein neumodisches Pflaster für ne alte Krücke zu
> sein?

Nein, an der richtigen Stelle eingesetzt, bekommen damit auch 
C-Programme so etwas wie Generizität, weil entsprechend der Plattform 
der am besten geeignetste DT gewählt wird (s.o.).

von (prx) A. K. (prx)


Lesenswert?

Guardians of the memory space schrieb:
> Seit wann gibst die überhaupt in der stdint.h? C11? C99?

stdint.h gibts seit C99.

> Scheint irgendein so ein neumodisches Pflaster für ne alte Krücke zu
> sein?

C11 ist neumodisches Pflaster für C99, was ein neumodisches Pflaster für 
C89/C90 war, das wiederum ein neumodisches Pflaster für die alte Krücke 
K&R-C war.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Guardians of the memory space schrieb:
> Also IMHO sollte der Compiler durch ne Speed-Optimierungsoption  dazu
> gebracht werden eine uint8_t - Variable automatisch durch eine
> schnellere ersetzen können

Das darf er und das tut er vielleicht auch, soweit das gleiche Ergebnis 
rauskommt. Im Speicher geht das üblicherweise nicht gut, also lässt er 
die Finger davon. Im Register wird schon mal eine andere Breite 
verwendet oder die Variable ganz wegoptimiert, wie bereits erwähnt.

Ohnehin: Da ARMe keine 8 Bit Register haben muss sich der Compiler 
aussuchen, ob er die übrigen 24 oder 56 Bits als Schrott betrachtet, 
oder ob er zero- oder sign-extended.

: Bearbeitet durch User
von Guardians of the memory space (Gast)


Lesenswert?

Frank M. schrieb:
> Guardians of the memory space schrieb:
>> Also IMHO sollte der Compiler durch ne Speed-Optimierungsoption  dazu
>> gebracht werden eine uint8_t - Variable automatisch durch eine
>> schnellere ersetzen können, auch wenn diese breiter als nötig ist.
>
> Ein Ändern des Typs durch globalen Rundumschlag halte ich für nicht
> sinnvoll. Das geht für Arrays (Größe!) oder für uint8_t-Variablen, bei
> denen der Überlauf von 255 auf 0 beabsichtigt ist, voll in die Hose.
> Außerdem beträfe so eine globale "Optimierungsoption" auch das Interface
> zur libc und anderen Bibliotheken.

Man muss ja die Optimierung nicht global benutzen sondern 
source-fileweise. Geht bei Makefiles und ähnlichen buildscripts 
wunderbar, GUI-User müssen eventuell mehr klicken um punktgenau 
optimieren zu können.

das Arrays problematisch sind ist mir bewußt deshalb schrieb ich ja auch 
von Variable und meinte damit ein wert in einem einzelnes Register. Und 
ob absichtliches Verwenden eines Überlaufes ein sauberer Programmierstil 
ist, wäre auch zu hinterfragen.

> das uint_fast8_t muss man schon sehr bewusst einsetzen und man
> muss wissen, was das für Konsequenzen haben kann, wie zum Beispiel
> fehlenden Überlauf. Ist also nix für Leute, die nicht nachdenken wollen.

Meinst Du damit den TS, der ja jedes Nachdenken dem Compiler überlassen 
will?

Beitrag #5060476 wurde vom Autor gelöscht.
von Adib (Gast)


Lesenswert?

Hallo Leute,

Danke für all eure Beiträge.

So wie es ausschaut brauche ich mich um diese Micro-Optimierungen keine 
Sorge machen. Das macht der Compiler für mich.
Wir sollten halt versuchen, den Code einheitlich zu gestalten, damit 
nicht mal hier eine Schleife von 0 hochzählt und dort eine vom Endwert 
runter und so was.

Bei Performance-Enpässen bzw. Interrupts schaue ich dann schon zweimal 
hin und gebe dem Compiler noch ein paar architekturbedingte Hinweise.

Alles klar und schönes WE.

Adib.
--

von Rolf M. (rmagnus)


Lesenswert?

Adib schrieb:
> Hallo Leute,
>
> Danke für all eure Beiträge.
>
> So wie es ausschaut brauche ich mich um diese Micro-Optimierungen keine
> Sorge machen. Das macht der Compiler für mich.

Genau. Außerdem spielt es in der Praxis nur selten eine Rolle, ob da ein 
Taktzyklus mehr für den Vergleich gebraucht wird oder nicht.
In der Regel ist nur ein sehr kleiner Teil des Code wirklich 
zeitkritisch.

Beitrag #5061757 wurde von einem Moderator gelöscht.
von c-hater (Gast)


Lesenswert?

Berufsrevolutionär schrieb:

> Naja hier ist der Compiler genauso unsmart wie der User.
> Integer ist der falsche Typ (16 bit) für den Schleifenindex
> (Wertebereich 10 bis 0), dadurch wird der Code langsamer und größer als
> nötig.

Genau. Das Problem ist hier eindeutig der User, nicht der Compiler. Das 
ist bei modernen Compilern sogar meistens der Fall.

Nur bei dem, womit moderne Compiler immer noch Probleme haben, kann man 
als Asm-Programmierer noch ernsthaft gegen sie punkten. Und das sind im 
Prinzip "nur" drei bis vier Problemkreise:

1)
Die Dinger optimieren leider nur ziemlich lokal. Insbesondere merklich 
bei nebenläufigem (oder quasi-nebenläufigem) Code. ISRs sind das 
typische Beispiel. Hier fehlt insbesondere die Abwägung der relativen 
Häufigkeit von ISR-Aufrufen und damit die Gewichtung der Anwendbarkeit 
von Optimierungen. Das ist ziemlich fatal, bei allem, was viele 
Interupts und trotzdem geringen Latenzen benötigt und allem, was 
Interrupts mit extrem hohem Durchsatz benötigt.

2)
Die Dinger kennen keine CPU-Flags (auf der Ebene des 
Hochsprachen-Quelltextes). Fatal für viele Anwendungen und die 
Implementierung nicht-nativer Datentypen.

3)
Die Dinger unterstützen i.d.R. nur eine halbe Handvoll Datentypen 
wirklich gut auf jedem Zielsystem, für das es sie gibt. Und selbst hier 
wird es oft schon ein wenig eng. Eigentlich sind nur vier Datentypen 
wirklich immer sehr gut bis perfekt unterstützt: int in der nativen 
Breite des Target und char. Beides jeweils in der signed und unsigned 
Variante. Der ganze Rest ist mehr oder weniger Glückssache, abhängig von 
Compiler und Target irgendwo zwischen sehr gut brauchbar bis reichlich 
lahm oder erst garnicht vorhanden.

4)
Die Dinger kennen bei den neuesten Devices i.d.R. noch längst nicht alle 
Tricks, die die Hardware schon beherrscht.

von YAG (Gast)


Lesenswert?

1
#include <cstdint>
2
volatile uint8_t i = 0;
3
4
int test (void){
5
#define LOOPS (10)
6
 for(int loop = LOOPS; loop != 0; loop--) {
7
    i++;
8
 }
9
}

Wenn ich das mit Matt Godbolt's Compiler Exporer übersetze bekomme ich 
mit clang 4 und -std=c++11 -O folgendes:
1
test():  
2
.LBB0_1:
3
        inc     byte ptr [rip + i]
4
        jmp     .LBB0_1
5
i:

Also eine Endlosschleife :)

Mögliche Änderungen: test in main umbenennen, void statt int als Return 
in der Signatur, return i ans Ende der Funktion

Siehe auch https://godbolt.org/g/AsaHVU

von Markus F. (mfro)


Lesenswert?

Jo, und das ist genau so korrekt.

Im Standard steht:

Flowing off the end of a function is equivalent to a return with no 
value; this results in undefined behavior in a value-returning function.

Der Compiler hätte auch alternativ das nächstgelegene Atomkraftwerk 
sprengen können; sei froh, daß er das nicht gemacht hat.

von Bernd K. (prof7bit)


Lesenswert?

Wilhelm M. schrieb:
> Sollte es C++ sein, so sollte man sich ++loop statt loop++ angewöhnen,
> falls man den urspünglichen Zustand nicht braucht.
> Auch wenn es für fundamentale Typen keinen Unterschied macht: in
> generischem Code kann das post-increment langsamer sein als das
> pre-increment

Ein Compiler aus dem Jahre 2017 sollte das erkennen und entsprechend 
optimieren können (das eigentliche Thema dieses Threads). Und ob es C++ 
oder C ist spielt dabei keine Rolle denn dieses eine Konstrukt verhält 
sich in beiden Sprachen gleich (und das Compilerbackend ist 
wahrscheinlich in weiten Teilen ohnehin das selbe), also gibt es keinen 
Grund warum das diesbezüglich irgend einen Unterschied machen sollte.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bernd K. schrieb:
> Wilhelm M. schrieb:
>> Sollte es C++ sein, so sollte man sich ++loop statt loop++ angewöhnen,
>> falls man den urspünglichen Zustand nicht braucht.
>> Auch wenn es für fundamentale Typen keinen Unterschied macht: in
>> generischem Code kann das post-increment langsamer sein als das
>> pre-increment
>
> Ein Compiler aus dem Jahre 2017 sollte das erkennen und entsprechend
> optimieren können (das eigentliche Thema dieses Threads). Und ob es C++
> oder C ist spielt dabei keine Rolle denn dieses eine Konstrukt verhält
> sich in beiden Sprachen gleich (und das Compilerbackend ist
> wahrscheinlich in weiten Teilen ohnehin das selbe), also gibt es keinen
> Grund warum das diesbezüglich irgend einen Unterschied machen sollte.

Ich schrieb oben: ... für fundamentale DT macht es keinen Unterschied 
...

Aber in C++ kann es für UDT ein Riesenunterschied sein.

von Bernd K. (prof7bit)


Lesenswert?

Wilhelm M. schrieb:
> Nein.
> Der Compiler macht genau das, was ich der Programmierer ihm sagt. Wenn
> da int als Typ steht, darf der Compiler nichts anderes nehmen.

Nein.

Wenn die Laufvariable keine Seiteneffekte hat und der Compiler beweisen 
kann daß sie einen bestimmten Wertebereich nicht übersteigt darf er 
nehmen was er will und was er für angemessen hält. Er darf sie dann 
sogar rückwärts zählen lassen wenn das für ihn angenehmer ist.

von Dieter F. (Gast)


Lesenswert?

Bernd K. schrieb:
> wenn das für ihn angenehmer ist.

Oh, Compiler haben Gefühle - interessant :-)

von Ordner (Gast)


Lesenswert?

Bernd K. schrieb:

> Wenn die Laufvariable keine Seiteneffekte hat

Sobald eine Variable im Speicher abgelegt wird sind Seiteneffekte nicht 
ausschliessbar. Und weiss man ob der später dazugelinkte IRQ-Handler 
nicht dreckigerweise Registerwerte zur schnellen Parameterübergabe 
nutzt?!

> und der Compiler beweisen
> kann

Ich wusste es! - der Compiler ist ein Arschloch das mir ständig was 
beweisen will ;-)


> daß sie einen bestimmten Wertebereich nicht übersteigt darf

Ja frag mal die Ariane5 Programmierer wie gut der Compiler im Abschätzen 
von Wertebereichen ist. https://de.wikipedia.org/wiki/Ariane_V88

> er
> nehmen was er will und was er für angemessen hält. Er darf sie dann
> sogar rückwärts zählen lassen

Klar und der Programmierer darf sich dann bei Realtime trace die Haare 
ausraufen, warum der µC plötzlich subtrahiert statt addiert.

von Oliver S. (oliverso)


Lesenswert?

Ordner schrieb:
> Sobald eine Variable im Speicher abgelegt wird sind Seiteneffekte nicht
> ausschliessbar.

Per Sprachdefinition sind sämtliche Seiteneffekte außerhalb der 
Sprachdefinition ausgeschlossen. Damit sind dem Compiler unbekannte 
Seiteneffekte vollständig ausschliesbar.

Für die anderen Fälle gibts volatile, das dem Compiler explizit sagt, 
das es da für ihn unbekannte Seiteneffekte geben könnte.

Ordner schrieb:
> Und weiss man ob der später dazugelinkte IRQ-Handler
> nicht dreckigerweise Registerwerte zur schnellen Parameterübergabe
> nutzt?!

Der später dazugelinkte IRQ-Handler nutzt deshalb keine Registerwerte 
zur Parameterübergabe, weil Parameter bei dem Konzept IRQ-Handler gar 
nicht möglich sind. Funktionsaufrufe aus dem Handler heraus folgen dem 
ABI, das kennt der Compiler.

Oliver

: Bearbeitet durch User
von Ordner (Gast)


Lesenswert?

Oliver S. schrieb:
> Ordner schrieb:
>> Sobald eine Variable im Speicher abgelegt wird sind Seiteneffekte nicht
>> ausschliessbar.
>
> Per Sprachdefinition sind sämtliche Seiteneffekte außerhalb der
> Sprachdefinition ausgeschlossen. Damit sind dem Compiler unbekannte
> Seiteneffekte vollständig ausschliesbar.

Ich definiere: "Es gibt keine Fehler" ....

Und wenn der Compiler nicht den gesamten Code zum compilieren bekommt, 
sondern auch sourcelose Teile die dazugelinkt werden?

> Für die anderen Fälle gibts volatile, das dem Compiler explizit sagt,
> das es da für ihn unbekannte Seiteneffekte geben könnte.

Auch da wo es nicht als volatile markiert ist kann es seiteneffekte 
geben. Das kann von dem Controller/OS abhängig sein unter der 
compilierte Code läuft (statt im geschützen Register landet die Variable 
bei Registerarmen Controllern auf dem anfälligen Stack, Fehler im 
Speicherereichsschutz des OS).

Einfach zu definieren - da gibt es nix ist IMHO recht blauäugig.

von Oliver S. (oliverso)


Lesenswert?

Je nun, wer auf fehlerhafte Implementierungen angewiesen ist, der hat 
Pech. Da ist dann prinzipiell alles möglich, und dagegen kann man dann 
auch nichts machen.

Das aber als Normalfall darzustellen, ist dann doch etwas übertrieben.

Oliver

von Oliver S. (oliverso)


Lesenswert?

Ordner schrieb:
> Und wenn der Compiler nicht den gesamten Code zum compilieren bekommt,
> sondern auch sourcelose Teile die dazugelinkt werden?

Ja, das ist natürlich ein Problem, das ...
...  irgendwie seit Urzeiten keins ist.
Denn das ist der Normalfall.

Wo ist denn da das Problem?

Oliver

: Bearbeitet durch User
von Ordner (Gast)


Lesenswert?

Oliver S. schrieb:
> Je nun, wer auf fehlerhafte Implementierungen angewiesen ist, der hat
> Pech. Da ist dann prinzipiell alles möglich, und dagegen kann man dann
> auch nichts machen.
>
> Das aber als Normalfall darzustellen, ist dann doch etwas übertrieben.


So ist es aber im real live - Seiteneffekte bestehen und lösen sich 
nicht in Luft aus nur weil man diese wegdefiniert. Und so mancher 
Programmierer der ein x-- schreibt checkt dann auch den Assemblercoder 
nach einem DEC und wirft den Compiler der da meint von sich aus das 
umdrehen zu können und INC auf den Müll. Weil im sicherheitsrelevanten 
Bereich will man Tools die sich deterministsich verhalten und nicht wie 
die Tools es für angemessen halten.

von (prx) A. K. (prx)


Lesenswert?

Ordner schrieb:
> will man Tools die sich deterministsich verhalten

Ich habe noch keinen Compiler gesehen, der sich nichtdeterministisch 
verhalten hätte. Aber schon oft welche, die deterministisch Code 
erzeugten, den der Programmierer nicht erwartete.

> und nicht wie die Tools es für angemessen halten.

Dann darfst du Optimierung nicht einschalten.

von Ordner (Gast)


Lesenswert?

A. K. schrieb:
> Ordner schrieb:
>> will man Tools die sich deterministsich verhalten
>
> Ich habe noch keinen Compiler gesehen, der sich nichtdeterministisch
> verhalten hätte. Aber schon oft welche, die deterministisch Code
> erzeugten, den der Programmierer nicht erwartete.

Ja weil eben der Sprachstandard da kein Soll setzte, beispielsweise beim 
"Register" keyword -> das darf de Compiler beachten muss aber nicht. 
Dann ist genau genommen nicht der Compiler undeterministsich sondern der 
Sprachstandard.

>> und nicht wie die Tools es für angemessen halten.
>
> Dann darfst du Optimierung nicht einschalten.

Genau! Hm, fing dieser Thread nicht mal mit der These "überlass den 
Compiler das Optimieren" an? Und jetzt die Forderung die Optimierung 
auszuschalten?!

von Oliver S. (oliverso)


Lesenswert?

Ordner schrieb:
> Dann ist genau genommen nicht der Compiler undeterministsich sondern der
> Sprachstandard.

Nein. Genaugenommen nutzt du bei Compilern und Systemen, die sich nicht 
entsprechend dem Sprachstadard verhalten, halt etwas zu der Sprache 
ähnliches, aber nicht die Sprache. In dem Fall halt "Similar C", "Fake 
C",  oder sowas, aber kein C.

>> Dann darfst du Optimierung nicht einschalten.
>
> Genau! Hm, fing dieser Thread nicht mal mit der These "überlass den
> Compiler das Optimieren" an? Und jetzt die Forderung die Optimierung
> auszuschalten?!

Siehe oben. Der Compiler darf jede Optimierung und Änderungen 
durchführen, die nach Definition des Sprachstandards zu den vom 
Programmierer vorgegeben Ausdrücken äquivalent sind.

Führt das zu unerwünschten Efekten, weil halt in "Similar C" 
programmiert wird, und nicht in C, dann muß man sich nicht über dn 
C-Standard beschweren.

Oliver

von Ordner (Gast)


Lesenswert?

Oliver S. schrieb:
> Siehe oben. Der Compiler darf jede Optimierung und Änderungen
> durchführen, die nach Definition des Sprachstandards zu den vom
> Programmierer vorgegeben Ausdrücken äquivalent sind.
>
> Führt das zu unerwünschten Efekten, weil halt in "Similar C"
> programmiert wird, und nicht in C, dann muß man sich nicht über dn
> C-Standard beschweren.

Nein, ich meine nich Similar C sondern Standard-C das es an einigen 
Stellen dem Compiler freistellt keywords zu ignorieren. Beispielweise 
"inline" wie dort: Beitrag "Re: Assembler (AVR) Freaks bitte: der schnellste Weg, einen ganzzahligen Wert zu skalieren?" 
dargestellt.

Ja der Compiler darf die Optimierung durchführen, muß aber nicht. Und 
das führt eben zu unterschiedlichen Code, was man nicht immer tolerieren 
kann - auch wenn der Code im Prinzip das selbe macht.

von (prx) A. K. (prx)


Lesenswert?

Ordner schrieb:
>> Dann darfst du Optimierung nicht einschalten.
>
> Genau! Hm, fing dieser Thread nicht mal mit der These "überlass den
> Compiler das Optimieren" an? Und jetzt die Forderung die Optimierung
> auszuschalten?!

Du hattest vorhin selbst gefordert, dass der Compiler in 
sicherheitkritischen Bereichen den Code Schritt für Schritt genau so 
erzeugen solle, wie es der Quellcode suggeriert. Das ist aber praktisch 
nur eine andere Formulierung für den Verzicht auf Optimierung.

Lässt man deine Forderung weg, wird Optimierung wieder zulässig. Man 
muss aber den Sprachstandard kennen und darf nicht erwarten, dass der 
Compiler eher der Intuition des Programmierers als dem Standard folgen 
wird.

Letztlich ist es die Frage, was man von der Programmiersprache erwartet. 
Ist es ein besserer Makroassembler, nur lesbarer und portabler. Oder ist 
es eine abstrahierende Hochsprache. Abstrahierend bedeutet, dass du 
nicht mehr genau weisst, was genau dabei als Befehlsablauf herauskommt.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Ordner schrieb:
> Ja weil eben der Sprachstandard da kein Soll setzte, beispielsweise beim
> "Register" keyword -> das darf de Compiler beachten muss aber nicht.

Das ist ein Relikt aus jenen Zeiten, in die Compiler ungefähr so 
arbeiteten, wie ich grad eben als "besserer Makroassembler" beschrieb. 
Sie also ohne nennenswerte Optimierungen über die Grenzen von Statements 
hinaus übersetzten (*).

Da in dieser Generation folglich auch keine Registeroptimierung 
stattfand, wurde die auf den Programmierer verlagert. Der Programmierer 
definierte, welche Variablen wichtiger sind als andere. Dass der 
Compiler das nicht zwingend beachten muss ergibt sich aus der 
unterschiedlichen dafür einsetzbaren Anzahl Register verschiedener 
Zielmaschinen.

Bei der PDP-11 waren das 3, bei der VAX wesentlich mehr, und bei einer 
Akku- oder Stack-Maschine hat man überhaupt keine. Es ergibt wenig Sinn, 
eine portabel gemeinte Sprache so definieren, dass alle Programme für 
jede Zielmaschine querbeet umgeschrieben werden müssen.

Dieses Keyword ist heute völlig bedeutungslos geworden und nur noch 
zwecks Kompatibilität mit altem Code noch vorhanden.

*: Weshalb es damals auch noch kein "volatile" gab. Man brauchte es 
schlichtweg nicht.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Ordner schrieb:
> Ja der Compiler darf die Optimierung durchführen, muß aber nicht. Und
> das führt eben zu unterschiedlichen Code, was man nicht immer tolerieren
> kann - auch wenn der Code im Prinzip das selbe macht.

Kein Compiler wird über die gesamte Lebensdauer eines Programms einen 
Quellcode in garantiert immer die gleiche Codesequenz übersetzen, wenn 
es zulässig ist, den Compiler upzudaten. Schon der erste Bugfix kann die 
Codesequenz ändern, und sei es nur indem er den falschen Code durch 
richtigen ersetzt.

Was du suchst hat reinweg nichts mit der offiziell definierten Sprache C 
zu tun. Nicht einmal mit einer beliebigen anderen Hochsprache. Wenn du 
die exakte Sequenz über die gesamte aktiv gepflegte Lebensdauer deines 
Programms reproduzierbar erhalten willst, gibt es nur eine zulässige 
Sprache: Assembler.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

A. K. schrieb:
> Wenn du
> die exakte Sequenz über die gesamte aktiv gepflegte Lebensdauer deines
> Programms reproduzierbar erhalten willst, gibt es nur eine zulässige
> Sprache: Assembler.

Noch nicht mal das.

Dann mußt Du auch archaische Prozessoren einsetzen.

Mit Register Renaming in superskalaren Prozessoren kannst Du nicht mehr 
davon ausgehen, daß dein Programm so ausgeführt wird, wie Du's 
geschrieben hast. Selbst wenn Du in Assembler programmierst. Und das 
kann beim nächsten Microcode-Upgrade schon wieder ganz anders aussehen.

von Rolf M. (rmagnus)


Lesenswert?

Ordner schrieb:
> So ist es aber im real live - Seiteneffekte bestehen und lösen sich
> nicht in Luft aus nur weil man diese wegdefiniert.

Umgekehrt wird ein Schuh draus: "Seiteneffekte" (furchtbare Übersetzung 
von "side effects") entstehen nicht einfach so aus dem Nichts, so dass 
Code grundsätzlich davon ausgehen müsste, dass sie jederzeit und überall 
unerwartet auftreten können. Wird nicht explizit definiert, dass es 
welche gibt, dann gibt es sie eben nicht, ganz ohne dass man sie dazu 
erst "wegdefinieren" müsste.

> Und so mancher Programmierer der ein x-- schreibt checkt dann auch den
> Assemblercoder nach einem DEC und wirft den Compiler der da meint von sich
> aus das umdrehen zu können und INC auf den Müll.

Wenn er nicht weiß, wie C funktioniert, ist das sein Problem und nicht 
die Schuld von C. Wer je schon mal von einem stark optimierenden 
Compiler erzeugten Code im Debugger im single-step-Modus durchlafen hat, 
hat eine gute Vorstellung davon, was der Compiler da alles umbaut.

von Wilhelm M. (wimalopaan)


Lesenswert?

Man kann die ganze Diskussion hier mit der "as-if"-Rule beenden:

http://en.cppreference.com/w/cpp/language/as_if

von Bernd K. (prof7bit)


Lesenswert?

Ordner schrieb:
> Klar und der Programmierer darf sich dann bei Realtime trace die Haare
> ausraufen, warum der µC plötzlich subtrahiert statt addiert.

Nein, normalerweise weiß man dass man beim Debugging von optimiertem 
Code oftmals rosa Elefanten sehen wird wo man grüne Nilpferde vermutet 
hätte. Aber es zählt nur was hinten rauskommt. Wenn ich einen Bug in 
meinem Algorithmus selbst vermute und nachvollziehen will wie das was 
ich da hingeschrieben habe ablaufen würde schalt ich die Optimierungen 
vorübergehend aus.

von Bernd K. (prof7bit)


Lesenswert?

Ordner schrieb:
> Auch da wo es nicht als volatile markiert ist kann es seiteneffekte
> geben.

Dann hat der Programmierer Scheiße gebaut weil er vergessen hat 
hinzuschreiben was sein Code eigentlich bewirken soll.

> So ist es aber im real live - Seiteneffekte bestehen und lösen sich
> nicht in Luft aus

Real-Live? Folgendermaßen funktioniert es reibungslos im Real-Life: 
Sachen die nur rumstehen aber offensichtlich keinem erkennbaren Zweck 
dienen und niemandem zu gehören scheinen fliegen kurzerhand raus denn 
der Compiler hat weder Zeit noch Platz zu verschwenden für toten oder 
unnötig umständlichen Code.

Wenn sie nicht rausfliegen sollen dann soll derjenige der das so haben 
will gefälligst einen entsprechenden Zettel drankleben, der Compiler 
kann ja sonst nicht riechen daß das unbenutzte Zeug noch irgendeinem 
Zweck dient.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.