Forum: Compiler & IDEs An alle Logiker


von Mensch Müller (Gast)


Lesenswert?

Habt ihr eine Idee, wie man das vereinfachen könnte, um es dem Compiler 
(avr-gcc) einfacher zu machen? Ich ziele auf einen möglichst kurzen Code 
ab. Ich denke, das Problem ist, dass die Variablen 32 Bit breit sind. 
Daher wären (wahrscheinlich) so wenige Vergleiche wie möglich am 
besten??
1
uint32 a, b, x;
2
3
// ...
4
5
if (a > b) {
6
  if ((x >= b) && (x < a)) return 1;
7
  return 0;
8
} else {
9
  if ((x >= a) && (x < b)) return 0;
10
  return 1;
11
}

von Christian B. (casandro)


Lesenswert?

Also ich denke diesen Code sollte Dein Compiler gut optimieren können, 
zumindest mit -Os. Lass Dir doch mal den Assemblercode ausgeben und 
schau mal nach was da raus kommt. Wir können hier jetzt viel 
diskutieren, was am Ende zählt ist was der Compiler wirklich real macht.

von Sina A. (sinapse)


Lesenswert?

1
return (a > b)? ((x >= b) && (x < a)):((x >= a) && (x < b));

aber denke auch, dass hier lesbarkeit wichtiger ist und der compiler das 
schon hinbekommt

lg

von tictactoe (Gast)


Lesenswert?

Erster Ansatz:
1
bool xa = x < a;
2
bool xb = x < b;
3
if (a > b)
4
  return xa && !xb;
5
else
6
  return xa || !xb;  // !(!xa && xb)
Musst selber ausprobieren, ob das was bringt.

von (prx) A. K. (prx)


Lesenswert?

Eine universelle Regel kann es schon deshalb nicht geben, weil sich 
nicht alle Maschinen gleich verhalten. Schlimmer noch: Die Effizient des 
Codes kann davon abhängen, wie die Wahrscheinlichkeit der Vergleiche 
ausfällt. Manche Compiler können aus einer Laufzeitanalyse des in der 
ersten Phase erstellten Programms schliessen, welche 
Optimierungsstrategie in der zweiten Phase sinnvoller ist.

von (prx) A. K. (prx)


Lesenswert?

tictactoe schrieb:
> Musst selber ausprobieren, ob das was bringt.

Yep. Beim avr-gcc sind in egal welcher Version eine Unzahl von 
Sprungbefehlen drin. Beim arm-gcc in der Originalversion einer, in 
deiner kein einziger.

von Nur geraten (Gast)


Lesenswert?

C kennt ja wohl nur bitweises XOR, kein logisches. Und 0 ist zwar false, 
aber true nicht unbedingt immer 1.

Aber so etwas könnte ja gehen?
1
return ((a > b) ? 0 : 1) ^ (((x >= b) && (x < a)) ? 1 : 0)

von Falk B. (falk)


Lesenswert?

@ Mensch Müller (Gast)

>Habt ihr eine Idee, wie man das vereinfachen könnte, um es dem Compiler
>(avr-gcc) einfacher zu machen?

Warum? Ist es zu komplex? Ist die Ausführungszeit zu groß?

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

> Ich ziele auf einen möglichst kurzen Code
>ab.

Der ist schon kurz.

> Ich denke, das Problem ist, dass die Variablen 32 Bit breit sind.
>Daher wären (wahrscheinlich) so wenige Vergleiche wie möglich am
>besten??

Du hast doch schon einen logischen Baum. Das kann so falsch nicht sein.
Deine Funktion prüft, ob x innerhalb der Grenzen a < x <= b liegt, wobei 
das Ganze auch laufen soll, wenn a und b vertauscht sind. OK. Braucht 
immer 3 Vergleiche. Die kriegt der avr gcc schon gut hin. Schau dir den 
ASM Output an.

von (prx) A. K. (prx)


Lesenswert?

Nur geraten schrieb:
> C kennt ja wohl nur bitweises XOR, kein logisches.

Bei 0/1 schon: !=

> (a > b) ? 0 : 1)

Das ist auch nix anderes als !(a > b) oder (a <= b).

von Mensch Müller (Gast)


Lesenswert?

Also, der Code wird nur ganz seltenst ausgeführt. Wie lange es dann 
braucht, ist eigentlich egal. Deswegen sollte der foot print im Flash so 
klein wie möglich sein.
Ich dachte, es gibt irgendeine fancy logische "Umformung", die ich 
übersehen habe.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Würd ich ähnlich hinschreiben wie tictac:
1
#include <stdint.h>
2
#include <stdbool.h>
3
4
bool in_interval (uint32_t x, uint32_t a, uint32_t b)
5
{
6
    bool ge_b = x >= b;
7
    bool lt_a = x < a;
8
9
    if (a > b)
10
        return ge_b & lt_a;
11
    else
12
        return lt_a | ge_b;
13
}

Am besten kannst bei sowas mit Inlining sparen, das vermeidet dann 
nämlich das Rumgeschaufel der Argumente.

Evtl. auch mal die Wirkung von -fno-split-wide-types begutachten.

Übrigens: wenn Werte mehrfach verwendet werden, z.B. wie bei min, max, 
etc. schneiden Inline-Funktionen gerne mal besser ab als Makros.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Meine Intuition sagt mir aber, dass es noch einen Trick gibt :-)

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:
> Meine Intuition sagt mir aber, dass es noch einen Trick gibt :-)

Noch besser als die Variante von "Nur geraten"?
  return (a <= b) ^ ((x >= b) && (x < a));

von Salewski (Gast)


Lesenswert?

Verwendet man bei bool-werten bitweises und/oder ? Hätte ich so nicht 
erraten, habe aber bool in C noch nie verwendet.

von asdfasd (Gast)


Lesenswert?

1
    return (x >= a) == (x < b);

Ist zwar lesbar aber nicht allzu verständlich ;-)

von Salewski (Gast)


Lesenswert?

Ah, den Trick muss ich mir mal merken. Ist schon verständlich wenn man 
es sich aufmalt.

von tictactoe (Gast)


Lesenswert?

asdfasd schrieb:
> return (x >= a) == (x < b);
>
> Ist zwar lesbar aber nicht allzu verständlich ;-)

Es wird ganz gut lesbar, wenn du dir folgendes als Idiom merkst: == bei 
Booleschen Werten bedeutet "genau dann wenn" (also Äquivalenz; != wäre 
dann Antivalenz).

von Peter D. (peda)


Lesenswert?

Nur geraten schrieb:
> Und 0 ist zwar false,
> aber true nicht unbedingt immer 1.

Falsch geraten, C muß immer 1 für True zurück liefern.
Nur wenn Du selber C einen Ausdruck übergibst, wertet es alles != 0 als 
True aus.
Braucht man die 1, kann man das so erzwingen:
1
!!(Ausdruck)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

asdfasd schrieb:
>
1
return (x >= a) == (x < b);

Leider falsch :-(

Für x < a < b liefert das Original 1, dein Ausdruck jedoch 0.

von Bülent C. (mirki)


Lesenswert?

Nutze doch das Karnaugh Diagramm dafür
https://de.wikipedia.org/wiki/Karnaugh-Veitch-Diagramm

von Stefan Salewski (Gast)


Lesenswert?

1
return (a <= b) ^ ((x >= b) && (x < a));

wäre dann also nicht weiter zu vereinfachen. Naja, immerhin nur noch 
eine Zeile, und für mich persönlich auch der Ausdruck mit der besten 
Lesbarkeit.

Wobei, in Ruby würde ich für die Lesbarkeit eventuell auch schreiben:
1
a, b = b, a if a < b
2
return x >= b && x < a

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan Salewski schrieb:
1
> return (a <= b) ^ ((x >= b) && (x < a));
> wäre dann also nicht weiter zu vereinfachen.

Das ist aber auch falsch:

Für x = a < b liefert das Original 0, dieser Ausdruck aber 1.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Mensch Müller (Gast)

>Also, der Code wird nur ganz seltenst ausgeführt. Wie lange es dann
>braucht, ist eigentlich egal. Deswegen sollte der foot print im Flash so
>klein wie möglich sein.

Du verschwendest sinnlos Zeit und Energie an der falschen Stelle. Lass 
es wie es ist, das ist lesbar. Fertig.

von Mensch Müller (Gast)


Lesenswert?

Sehe ich nicht so. Ich habe schon durch unzählige kleinere Optimierungen 
Platz für neue Features geschaffen, die sonst nicht in den begrenzten 
Flash gepasst hätten.

von Falk B. (falk)


Lesenswert?

@Mensch Müller (Gast)

>Sehe ich nicht so. Ich habe schon durch unzählige kleinere Optimierungen
>Platz für neue Features geschaffen, die sonst nicht in den begrenzten
>Flash gepasst hätten.

Dann hast du den falschen Controller/Flashgröße gewählt. Bestenfalls bei 
Stückzahlen von 10k++ ist sowas sinnvoll.

von Carl D. (jcw2)


Lesenswert?

Mensch Müller schrieb:
> Sehe ich nicht so. Ich habe schon durch unzählige kleinere
> Optimierungen Platz für neue Features geschaffen, die sonst nicht in
> den begrenzten Flash gepasst hätten.

Neu, Moby gibt's nun auch in C!
;-)

Bevor man aber seine Source unlesbar macht, sollte man ALLE 
Kommandozeilenregister des avr-gcc gezogen haben.

von Salewski (Gast)


Lesenswert?

Falk B. schrieb:
> Bestenfalls bei
> Stückzahlen von 10k++ ist sowas sinnvoll.

Ist schon ganz gut auch mal etwas nachzudenken. Der ursprüngliche Code 
ganz oben sieht für mich jedenfalls recht hässlich aus. Dass der 
Compiler das wahrscheinlich eh gut optimiert ist klar. Dass es auf 
wenige eingesparte Bytes nicht ankommen kann ist eh klar, und auf einige 
eingesparte Taktzyklen wohl in der Regel auch nicht. Ich hatte bei der 
Umformung nur geraten ohne wirklich nachzudenken -- man muss da schon 
etwas aufpassen. Mit einer ähnlichen, scheinbar trivialen, aber falschen 
Umformung habe ich kürzlich einen halben Tag vergeudet :-(

von Mensch Müller (Gast)


Lesenswert?

Naja, das Projekt ging mit dem mega168 los, weil es was anderes nicht 
gab! Stückzahl ist 1000+, wir würden aber noch gerne Updates anbieten. 
Warum auch nicht, funktioniert ja alles.

Kommandozeilenparameter haben wir uns ganz besonders intensiv gewidmet, 
haben sogar letztendlich ein Script geschrieben, das >200 
avr-gcc-Optionen einzeln durchprobiert, um herauszufinden, was etwas 
bringt und was nicht. Die paar Optionen, die etwas bringen haben wir 
dann (teilweise) kombiniert. Manche wirkten gegeneinander, die flogen 
dann natürlich raus.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Carl D. schrieb:
> Bevor man aber seine Source unlesbar macht,

??? Sogar Assembler bietet die Möglichkeit, Code zu kommentieren:

Und ja, zur Beschreibung darf man auch Pseudo-Code, mathematische 
Notation und ineffizientere aber einfacher zu erfassende Codesequenzen 
verwenden.

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Johann L. schrieb:
> ??? Sogar Assembler bietet die Möglichkeit, Code zu kommentieren:
>
> Und ja, zur Beschreibung darf man auch Pseudo-Code, mathematische
> Notation und ineffizientere aber einfacher zu erfassende Codesequenzen
> verwenden.

Zu solchen eher kryptischen Compiler-Überredungs-Konstrukten Gesellen 
aber oftmals "Leerkommentare".

@Mensch Müller:
Auch LTO? Dabei ist nicht nur wichtig, daß man -flto an Compiler und 
Linker gibt, man muß auch das -Os an beide geben.
Ich hab das nicht so systematisch durchgetestet, aber bin von der "ich 
optimier mal so, als ob alles in einem File stehen würde"-Method in der 
Regel begeistert.

von Mensch Müller (Gast)


Lesenswert?

Ja, natürlich -flto, gerade -flto bringt viel!

Man kann das alles in einem Rutsch machen:

avr-gcc -xc *.c [...] -o output.elf

Auch haben wir verschiedene gcc-Versionen durchgetestet, manche bringen 
mehr, manche weniger, haben merkwürdige Bugs oder brauchen andere 
Parameterkombinationen!

Hier schwirrt z.B. eine 5.2.1 herum, aber die ist teilweise kaputt: Man 
kann -flto und -fipa-pta nicht kombinieren, was eventuell etwas bringen 
könnte.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Sollte die Zeile
1
if (a > b) {

nicht
1
if (a >= b) {

heißen?

Dann könnte man den Code nämlich umschreiben in
1
return x-b < a-b;

Das ist nicht nur im Quellcode kürzer, sondern spart auch im Binärcode
einige Bytes. Hier sind die beiden Alternativen jeweils in eine Funktion
verpackt:

1
#include <stdio.h>
2
#include <stdint.h>
3
4
uint8_t f1(uint32_t a, uint32_t b, uint32_t x) {
5
  if (a >= b) {
6
    if ((x >= b) && (x < a))
7
      return 1;
8
    return 0;
9
  }
10
  else {
11
    if ((x >= a) && (x < b))
12
      return 0;
13
    return 1;
14
  }
15
}
16
17
uint8_t f2(uint32_t a, uint32_t b, uint32_t x) {
18
  return x-b < a-b;
19
}

1
$ avr-gcc-4.7.4 -mmcu=atmega8 -Wall -Os -ffunction-sections -c test.c
2
$ avr-size -A test.o
3
test.o  :
4
section    size   addr
5
.text         0      0
6
.data         0      0
7
.bss          0      0
8
.text.f1     78      0
9
.text.f2     50      0
10
.comment     18      0
11
Total       146

Die Funktion f2 spart gegenüber f1 also mehr als ein Drittel Flash ein.
Die neueren GCCs ab 4.9 schneiden da leider nicht mehr ganz so gut ab.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mensch Müller schrieb:
> Hier schwirrt z.B. eine 5.2.1 herum, aber die ist teilweise kaputt:
> Man kann -flto und -fipa-pta nicht kombinieren, [...]

Ist für die 5.3 behoben:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66705#c7

Momentan ist GCC für die 5.3 eingefroren, d.h. es kann sich nur noch um 
wenige Tage handeln bis zur Release 5.3.

Yalu X. schrieb:
> Sollte die Zeile
1
> if (a > b) {
> nicht
1
> if (a >= b) {
> heißen?

Hab ich mich auch schon gefragt; scheint aber um ein halboffenes 
Intervall zu gehen, und das ist leer für a = b.

> Dann könnte man den Code nämlich umschreiben in
1
> return x-b < a-b;

Das sind aber immer noch 3 32-Bit Operationen (2 falls a und b zur 
Compilezeit bekannt sind).  Aber besser gehts wohl nicht.

Übrigens ist das GCCs Methode, um zu testen, ob ein Wert X in einem 
Intervall [B,A] liegt falls B und A bekannt sind:
Aus
1
if (X >= B && X <= A)
wird
1
XB = X - B;
2
if (XB <= (unsigned) A-B)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich sehe gerade, dass es auch mit
1
if (a > b) {

geht.

Der gekürzte Code lautet dann:
1
return x-a >= b-a;

Der Binärcode wird dadurch sogar noch 2 Bytes kürzer:

1
$ avr-gcc-4.7.4 -mmcu=atmega8 -Wall -Os -ffunction-sections -c test.c
2
$ avr-size -A test.o
3
test.o  :
4
section    size   addr
5
.text         0      0
6
.data         0      0
7
.bss          0      0
8
.text.f1     78      0
9
.text.f2     48      0
10
.comment     18      0
11
Total       144

: Bearbeitet durch Moderator
von Mensch Müller (Gast)


Lesenswert?

Wir warten sehnsüchtigst auf eine Windows-Version der 5.3

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> Das sind aber immer noch 3 32-Bit Operationen (2 falls a und b zur
> Compilezeit bekannt sind).  Aber besser gehts wohl nicht.

Ja, aber für den Originalcode erzeugt der Compiler 5 32-Bit-Vergleiche
(von denen allerdings maximal 3 ausgeführt werden).

Selbst wenn man die Vergleiche in der drittletzten Zeile logisch so
umformt, dass jeweils zwei Vergleiche sichtbar doppelt auftreten (also
!(x<a) statt x>=a und !(x>=b) statt x<b), wird das Ergebnis nicht
besser.

Man könnte, um die Anzahl der Vergleiche zu reduzieren, die Ergebnisse
von x>=b und x<a in Variablen zwischenspeichern, etwa so:

1
uint8_t f0(uint32_t a, uint32_t b, uint32_t x) {
2
  uint8_t xb = x>=b, xa = x<a;
3
  return a > b ? xb && xa : xa || xb;
4
}

Jetzt erzeugt der Compiler tatsächlich nur noch 3 32-Bit-Vergleiche, der
Binärcode wird aber sogar noch größer, er wächst von 78 auf 86 Bytes
(GCC 4.7.4)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Carl D. schrieb:
> Johann L. schrieb:
>> ??? Sogar Assembler bietet die Möglichkeit, Code zu kommentieren:
>>
>> Und ja, zur Beschreibung darf man auch Pseudo-Code, mathematische
>> Notation und ineffizientere aber einfacher zu erfassende Codesequenzen
>> verwenden.
>
> Zu solchen eher kryptischen Compiler-Überredungs-Konstrukten Gesellen
> aber oftmals "Leerkommentare".

ad Leer-Kommentar:

Wer seinen eigenen Code nicht mehr versteht — insbesondere weil er nicht 
kommentiert / dokumentiert — ist selbst Schuld.  Im professionellen 
Bereich ist ernsthaftes Code-Reviewing ebenso wichtig wie Spezifikation 
und Testing.

ad Compiler-Überredungs-Konstrukten:

Compiler sind keine Computer-Algebra-Systeme und werden IMHO in 
absehbarer Zeit auch keine CAs darstellen, enthalten bzw. anbinden.

In diesem Sinne sehe ich die Suche nach besseren Formulierungen ähnlich 
wie die Suche nach besseren Algorithmen bzw. Datenstrukturen.  Ein 
Compiler wird wird dir z.B. auch kein Bubblesort durch ein Quicksort 
ersetzen.

: Bearbeitet durch User
von Falk S. (db8fs)


Lesenswert?

Hab auch noch 'nen Ansatz:
1
inline void swap( uint32_t* a, uint32_t* b )
2
{
3
    uint32_t tmp = *a;
4
    *a = *b;
5
    *b = tmp;
6
}
7
8
uint32_t myfun( uint32_t a, uint32_t b, uint32_t x)
9
{
10
    int result = (a <= b);
11
12
    if( result )
13
    {
14
        swap( &a, &b );
15
    }
16
17
    if( x >= a && x < b )
18
        return !result;
19
20
    return result;
21
}

Liefert mir übersetzt mit
1
avr-gcc -mmcu=atmega8 -Wall -Os -ffunction-sections -c test2.c -std=c99
1
falk@thinkpad /tmp $ avr-size -A test.o
2
test.o  :
3
section       size   addr
4
.text            0      0
5
.data            0      0
6
.bss             0      0
7
.text.myfun     92      0
8
.comment        43      0
9
Total          135

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Der gekürzte Code lautet dann:
1
> return x-a >= b-a;

Ha, wusst ich's doch.  Wo's bei mir nur bis zur Intuition reicht kommt 
Yalu alsbald mit der Inkarnation. :-)

Mensch Müller schrieb:
> Wir warten sehnsüchtigst auf eine Windows-Version der 5.3

Den hab ich mir sellemols unter Linux selbst compiliert. Alles was für 
den Canadian-Cross benötigt wird ist ein linux-mingw32 Cross-Compiler — 
und natürlich eine (virtuelle) Linux Maschine.

von 老手 (Gast)


Lesenswert?

Nur geraten schrieb:
> Und 0 ist zwar false, aber true nicht unbedingt immer 1.

Man muss in C schon dankbar sein, true immer !false ist.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> In diesem Sinne sehe ich die Suche nach besseren Formulierungen ähnlich
> wie die Suche nach besseren Algorithmen bzw. Datenstrukturen.  Ein
> Compiler wird wird dir z.B. auch kein Bubblesort durch ein Quicksort
> ersetzen.

Du müsstest dich nur mal hinsetzen und so etwas in den GCC einbauen ;-)

Schon der Fortran-Compiler der Cray-Vektorrechner vor mehr als 30 Jahren
hat vom Programmierer implementierte Funktionen mitunter automatisch
durch eigene Funktionen ersetzt. Man las dann Meldungen ähnlich dieser
(den genauen Wortlaut auf Englisch weiß ich leider nicht mehr):

1
Hey, wie ich sehe, hast du gerade versucht, eine Funktion für die
2
Matrizenmultiplikation zu schreiben. Das ist zwar gut gemeint, aber ich
3
habe eine viel bessere in meiner Bibliothek, die ich anstelle der deinen
4
verwenden werde.

von Falk B. (falk)


Lesenswert?

@Salewski (Gast)

>> Bestenfalls bei
>> Stückzahlen von 10k++ ist sowas sinnvoll.

>Ist schon ganz gut auch mal etwas nachzudenken.

Dagegen ist nix einzuwenden. Aber Krampfoptimierungen nach Gefühl sind 
meistens Schrott. Man wirft den Schinken nach der Wurst.

>Der ursprüngliche Code
>ganz oben sieht für mich jedenfalls recht hässlich aus.

Bitte? Der ist doch sonnenklar?

>Dass der
>Compiler das wahrscheinlich eh gut optimiert ist klar. Dass es auf
>wenige eingesparte Bytes nicht ankommen kann ist eh klar,

So klar ist das gar nicht, zumindest nicht für den OP!

>etwas aufpassen. Mit einer ähnlichen, scheinbar trivialen, aber falschen
>Umformung habe ich kürzlich einen halben Tag vergeudet :-(

Womit du deinen Einwand ad absurdum führtst!

Ich verweise nochmals auf

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

Mit der Betonung auf

"Verfrühte Optimierung ist die Wurzel allen Übels"

von Falk B. (falk)


Lesenswert?

@ Yalu X. (yalu) (Moderator)

>Dann könnte man den Code nämlich umschreiben in

>return x-b < a-b;

Lesbarkeit?

>Total       146

>Die Funktion f2 spart gegenüber f1 also mehr als ein Drittel Flash ein.

Satte 50 Bytes!

>Die neueren GCCs ab 4.9 schneiden da leider nicht mehr ganz so gut ab.

Aha.

von Falk S. (db8fs)


Lesenswert?

Nochmal auf C++ mit avr-gcc-4.9.3, wie oben unter Annahme, dass wirklich 
die echt-kleiner Operation notwendig ist:
1
#include <stdint.h>
2
3
template<typename T>
4
void swap( T & a, T & b)
5
{
6
    T tmp(a);
7
    a = b;
8
    b = tmp;
9
}
10
11
bool inInterval( uint32_t a, uint32_t b, uint32_t x)
12
{
13
    bool inverted(a > b);
14
15
    if( inverted )
16
    {
17
        swap( a, b );
18
    }
19
20
    if( x >= a && x < b )
21
        return !inverted;
22
23
    return inverted;
24
}

Kommt total bei 131 raus. Die einzige Kopplung ist halt noch an die 
bool-Variable, aber vielleicht findet man da auch noch 'nen besseren 
Bezeichner dafür. Sollte aber schon recht lesbar sein und halbwegs 
beschreiben, was eigentlich genau die Funktion leistet.

von Stefan Salewski (Gast)


Lesenswert?

Falk B. schrieb:
>>Der ursprüngliche Code
>>ganz oben sieht für mich jedenfalls recht hässlich aus.
>
> Bitte? Der ist doch sonnenklar?

Klar ist der klar -- aber hässlich nach meiner Ansicht. Das errinnert 
mich irgendwie an Java in der Anfängervorlesung.

Yalus Umformung ist wirklich beachtlich -- vielleicht nicht so sehr für 
die Praxis, aber für das Grundsätzliche. Auch ein sehr schönes Beispiel 
für einen Programmierkurs.

von Falk B. (falk)


Lesenswert?

@ Stefan Salewski (Gast)

>> Bitte? Der ist doch sonnenklar?

>Klar ist der klar

DAS REICHT! Und ist viel wichtiger als B-Note!

> -- aber hässlich nach meiner Ansicht. Das errinnert
>mich irgendwie an Java in der Anfängervorlesung.

Jaja, zu trivial. Was soll man dann auch an so einer "Funktion" 
optimieren. Da gibt es keine Blumentopf zu gewinnen!

>Yalus Umformung ist wirklich beachtlich --

Wirklich?

> vielleicht nicht so sehr für
>die Praxis, aber für das Grundsätzliche.

Aha. Der Schöngeist spricht. Aber der erleidet in der PRAXIS nur 
allzuoft SCHIFFBRUCH!

> Auch ein sehr schönes Beispiel für einen Programmierkurs.

Obfuscated C! :-(

von Stefan Salewski (Gast)


Lesenswert?

Falk B. schrieb:
> Aha. Der Schöngeist spricht.

Weißt Du noch, als Gauss in der Schule die Zahlen 1 bis 100 addieren 
sollte?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Falk B. schrieb:
> @ Yalu X. (yalu) (Moderator)
>
>>Dann könnte man den Code nämlich umschreiben in
>
>>return x-b < a-b;
>
> Lesbarkeit?

Ich zitiere mal asdfasd:

asdfasd schrieb:
> Ist zwar lesbar aber nicht allzu verständlich ;-)

In einem realen Programm gehört deswegen noch ein Kommentar dazu.

Das gilt aber auch für den Originalcode:

Lesbar ist er ohne Zweifel (auch wenn das Lesen wegen der vielen
Vergleichsoperationen und Returns deutlich länger dauert als bei meinem
Code).

Verständlich ist er ohne Kommentar aber auch nicht, wie an den vielen
Fehlversuchen, den Code zu vereinfachen, unschwer zu erkennen ist. Auch
ich habe ihn zunächst falsch verstanden und bin deswegen in dieselbe
Falle gestolpert wie asdfasd:

asdfasd schrieb:
> return (x >= a) == (x < b);

Verstanden hat man so ein Konstrukt erst, wenn man in wenigen Worten,
losgelöst vom Quellcode und möglichst ohne mathematische Formalismen
umschreiben kann, was es tut. Das ist aber bei diesem Beispiel gar nicht
so einfach.

von Falk B. (falk)


Lesenswert?

@Stefan Salewski (Gast)

>> Aha. Der Schöngeist spricht.

>Weißt Du noch, als Gauss in der Schule die Zahlen 1 bis 100 addieren
>sollte?

Ja, der war drei Klassen unter mir ;-)

JA, DORT ist es WIRKLICH sinnvoll gewesen, vor allem ohne Computer!

von Eric B. (beric)


Lesenswert?

Falk B. schrieb:
>>Weißt Du noch, als Gauss in der Schule die Zahlen 1 bis 100 addieren
>>sollte?
>
> Ja, der war drei Klassen unter mir ;-)

Die Schule war in einem vier-stockigen Gebäude? ;-)

von Paul B. (paul_baumann)


Lesenswert?

Eric B. schrieb:
>> Ja, der war drei Klassen unter mir ;-)
>
> Die Schule war in einem vier-stockigen Gebäude? ;-)

Dann kam ich in die Schule,
ich konnte nichts dafür:
Stand meistens nur im Klassenbuch
oder draußen vor der Tür.
;-)
MfG Paul

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk B. schrieb:
> "Verfrühte Optimierung ist die Wurzel allen Übels"

Die Wurzel allen Übels?

Das ist doch Käse — auch wenn's von Knuth stammt.

Ich hab schon Kunden gesehen (> 1e10€ Jahresumsatz) die sind so 
Unterkante Oberkiefer mit der Performance ihere Software dass sie aus 
Verzweiflung sogar neue µCs designen lassen und einsetzen.  Nur weil die 
Software so am Arcsh und so fukcing unwartbar ist und "Design"-Pattern 
benutzt wo es jedem Hobby-Programmierer schlecht wird.  Irgendwelche 
"Design"-Entscheidungen von vor xx Jahren in einem absolut unwartbaren 
Hack mit Soße, und keiner deren BWL-Entscheider Fuzzis hat die Eier in 
der Hose um das Zeug auf ein tragfähiges Design zu portieren oder den 
Rotz komplett in die Tonne zu treten.  MISRA einsetzen und dann 
Toolchains mit Hacks aufbohren lassen, damit das Zeugs nicht die 
Zertifizierung verliert.  Also komm mit nicht mit der Wurzel allen 
Übels!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

s/hab/hatte/

von lalala (Gast)


Lesenswert?

klärt mich auf! Ist nicht

return x-a >= b-a

dasselbe wie

return x >= b

und damit nicht richtig.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Du rechnest weder in R noch in Z noch in Q sondern in Z mod mZ.  Aber 
zum Merken ist der Zusammenhang gut.  Und ja, im Endeffekt ist die 
Renormierung auf a=0 die Idee hinter der Umformung.

von Falk B. (falk)


Lesenswert?

@Johann L. (gjlayde) Benutzerseite

>> "Verfrühte Optimierung ist die Wurzel allen Übels"

>Die Wurzel allen Übels?

Im philosophischen Sinne ;-)

>Das ist doch Käse — auch wenn's von Knuth stammt.

Es ist nicht absolut gemeint.

>Ich hab schon Kunden gesehen (> 1e10€ Jahresumsatz) die sind so
>Unterkante Oberkiefer mit der Performance ihere Software dass sie aus
>Verzweiflung sogar neue µCs designen lassen und einsetzen.  Nur weil die
>Software so am Arcsh und so fukcing unwartbar ist und "Design"-Pattern
>benutzt wo es jedem Hobby-Programmierer schlecht wird.

Dort steht aber nicht die Frage nach Optimierung sondern nach AUFRÄUMEN 
und HAUSAUFGABEN machen!

>  Irgendwelche
>"Design"-Entscheidungen von vor xx Jahren in einem absolut unwartbaren
>Hack mit Soße, und keiner deren BWL-Entscheider Fuzzis hat die Eier in
>der Hose um das Zeug auf ein tragfähiges Design zu portieren oder den
>Rotz komplett in die Tonne zu treten.  MISRA einsetzen und dann
>Toolchains mit Hacks aufbohren lassen, damit das Zeugs nicht die
>Zertifizierung verliert.  Also komm mit nicht mit der Wurzel allen
>Übels!

;-)

Wenn dich das Thema dermaßen berührt darfst du auch den Artikel bzw. die 
Formulierung ändern.

von Horst S. (Gast)


Lesenswert?

Kann man die Logik der Abfrage nicht umkehren?
1
return !((x >= a && x >= b) || (x < a && x < b));

(Not yet tested)

von Mensch Müller (Gast)


Lesenswert?

Interessanterweise macht die Reihenfolge der Operanden in der if-Abfrage 
(a && b) vs. (b && a) oft einen Größenunterschied aus. gcc scheint keine 
Optimierung durch Vertauschungsversuche durchzuführen. Zugegeben, es 
wäre sicher eine Herausforderung, die Fälle zu erkennen, in denen die 
Reihenfolge wichtig ist (z.B. bei volatile oder Funktionsaufrufen).

von Bauteiltöter (Gast)


Lesenswert?

Soweit ich weiß darf der Compiler die beiden Teile einer &&-Verknüpfung 
gar nicht umdrehen da der Standard garanteriert, das bei
1
if( a() && b()) )
2
{...}

b() nicht ausgeführt wird wenn a() schon false ergibt.

von Mensch Müller (Gast)


Lesenswert?

Ja, das meinte ich. Auch bei

if (a() || b()))

wird ja b nicht ausgeführt, wenn a() schon true ist.

Aber bei normalen non-volatile Variablen ginge es eben problemlos. Und 
da tritt auch eine Größendifferenz auf.

von Mensch Müller (Gast)


Lesenswert?

(in gewissen Fällen)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jaja, so ist das. Andere Programme können zu anderem Code führen...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk B. schrieb:
> @Johann L. (gjlayde) Benutzerseite
>> Ich hab schon Kunden gesehen (> 1e10€ Jahresumsatz) die sind
>> so Unterkante Oberkiefer mit der Performance ihrer Software dass
>> sie aus Verzweiflung sogar neue µCs designen lassen und einsetzen.
>>  Nur weil die Software so am Arcsh und so fukcing unwartbar ist und
>> "Design"-Pattern benutzt wo es jedem Hobby-Programmierer schlecht
>> wird.
>
> Dort steht aber nicht die Frage nach Optimierung sondern nach
> AUFRÄUMEN und HAUSAUFGABEN machen!

Eine Software, die zig MB Binärcode erzeugt und von x anderen Firmen 
mitverwendet wird (und die auch noch entsprechend Code zuprogrammierten) 
"räumst" du nicht einfach mal so auf.  Vor allem wenn die Software 
millionanfach auf der Straße rumfährt...

Nur weil irgendein Techie mal ne Schnapsidee hatte, es offenbar keine 
(funktionierenden) Review-Prozesse gibt, die Hybris vorherrscht mit 
Knete sei alles machbar, usw.

Die Wurzel eines Übels steckt ganz ganz oft im Design, und das Design 
ist eines der ersten Dinge bei der Softwareentwicklung.  Das wollte 
ich damit ausdrücken.

Und auch Knuth hat mit seinen Algorithmen und Datenstrukturen wichtige 
Beiträge zum Design geliefert.  Von daher finde ich solche absoluten 
Zitate von überaus zweifelhaftem Wert — und mit Philosophie hat es auch 
nix zu tun.

>> MISRA einsetzen und dann Toolchains mit Hacks aufbohren lassen,
>> damit das Zeugs nicht die Zertifizierung verliert.  Also komm
>> mir nicht mit der Wurzel allen Übels!
>
> Wenn dich das Thema dermaßen berührt

Sorry, mir platzt auch mal die Hutschnur, auch wenn's nur indirekt mit 
diesem Thread zu tun hat.

von Falk B. (falk)


Lesenswert?

@ Johann L. (gjlayde) Benutzerseite

>Eine Software, die zig MB Binärcode erzeugt und von x anderen Firmen
>mitverwendet wird (und die auch noch entsprechend Code zuprogrammierten)
>"räumst" du nicht einfach mal so auf.  Vor allem wenn die Software
>millionanfach auf der Straße rumfährt...

Schon klar. Aber was wäre dein Vorschlag?

>Die Wurzel eines Übels steckt ganz ganz oft im Design, und das Design
>ist eines der ersten Dinge bei der Softwareentwicklung.  Das wollte
>ich damit ausdrücken.

Ach so. Kein Einspruch. Gilt das nicht fast überall?

>Und auch Knuth hat mit seinen Algorithmen und Datenstrukturen wichtige
>Beiträge zum Design geliefert.

Du meinst dein Problemdesign?

> Von daher finde ich solche absoluten
>Zitate von überaus zweifelhaftem Wert

Wenn's dir nicht passt, formulier es um. Es ist nicht in Stein 
gemeißelt.
Ich bin da auch kein Fanatiker und ich hab das bestenfalls 
abgeschrieben.

>> Wenn dich das Thema dermaßen berührt

>Sorry, mir platzt auch mal die Hutschnur, auch wenn's nur indirekt mit
>diesem Thread zu tun hat.

Hast du darüber schon mit deinem Therapeuten gesprochen?
Oder wenigstens ein Code review mit ihm gemacht?
;-)

von Mensch Müller (Gast)


Lesenswert?

Also, ich würde das als das selbe Programm bezeichnen, egal mit #if 1 
oder #if 0
Zugegeben, hier ist der Unterschied im Binary winzig. Meist gewinne ich 
aber je optimierter if-Abfrage etwa 2-6 Bytes.
1
#include <avr/io.h>
2
3
static uint8_t max_day_in_month(uint8_t year, uint8_t month) {
4
  static const __flash uint8_t MAX_DAY_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
5
  uint8_t d = MAX_DAY_IN_MONTH[month-1];
6
  #if 1
7
  if ((month == 2) && (year & 4)) d++;
8
  #else
9
  if ((year & 4) && (month == 2)) d++;
10
  #endif
11
  return d;
12
}
13
14
int __attribute__((OS_main)) main(void) {
15
  GPIOR0 = max_day_in_month(GPIOR1, GPIOR2);
16
17
  while (1);
18
}

von Mensch Müller (Gast)


Lesenswert?

& 4 ist natürlich Nonsens, hier geht es nur um die Tatsache, dass man 
durch Vertauschung von Operanden Code einspart.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dei Programme sind aber nicht gleich.  Die Funktion wird statisch nur 1x 
aufgerufen, daher geinlint, und die GPIORx sind nun mal volatile.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

...Aber was willst du und mit dem Code sagen?

Für anderen Code bekommst du i.d.R. ein anderes Binary.

Für äquvalenden Code bekommst du ein äquivalentes Binary.

Zwei äquivalentes Binaries sind i.d.R. nicht binärgleich.

von Mensch Müller (Gast)


Lesenswert?

Hm, aber die Operation d++ findet ja nicht auf eine volatile Variable 
statt, oder? Selbst, wenn es geinlined wird.

Ich vermute etwas anderes, weil ich es jetzt schon mehrfach beobachtet 
habe:
Wenn man eine Variable benutzt, welche kurz zuvor im Einsatz war, dann 
wird es kleiner. Hier ist es month. Das wird in der Zeile vorher 
benutzt. Wenn es in der if-Abfrage ganz links steht, scheint der 
Compiler es noch in einem Register parat haben zu können. Dreht man es 
um, wird das Register durch day wiederverwendet und muss in einem 
zusätzlichen Schritt neu geholt werden.
Ich habe mir den Assemblercode noch nicht angesehen, aber zumindest ist 
das Schema immer das Gleiche. Schiebt man Variablen, die kurz vorher 
benutzt wurden, an die erste Stelle, spart man Code.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mensch Müller schrieb:
> Hm, aber die Operation d++ findet ja nicht auf eine volatile Variable
> statt, oder?

Das nicht, aber die Vergleiche sind gegen Volatile, und um vergleichen 
zu können muss erst gelesen werden, und du kannst nicht einfach mal so 
ein Volatile auf Verdacht lesen.

: Bearbeitet durch User
von Mensch Müller (Gast)


Lesenswert?

Wenn ich dich richtig verstehe, wird in folgenden Beispiel UDR0 im Fall 
#if 1 nur bei (month == 2) gelesen und im Fall #if 0 jedes Mal.
1
#include <avr/io.h>
2
3
static uint8_t max_day_in_month(uint8_t year, uint8_t month) {
4
  static const __flash uint8_t MAX_DAY_IN_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
5
  uint8_t d = MAX_DAY_IN_MONTH[month-1];
6
  #if 1
7
  if ((month == 2) && (year & 4)) d++;
8
  #else
9
  if ((year & 4) && (month == 2)) d++;
10
  #endif
11
  return d;
12
}
13
14
int __attribute__((OS_main)) main(void) {
15
  uint8_t y = UDR0;
16
  uint8_t m = GPIOR1;
17
  GPIOR0 = max_day_in_month(y, m);
18
19
  while (1);
20
}

Korrekt?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mensch Müller schrieb:
> Korrekt?

Nein.  Die Reihenfolge der Auswertung von Funktionsparametern ist nicht 
spezifiziert.

von Mensch Müller (Gast)


Lesenswert?

Aber dann sind die beiden Zeilen doch tatsächlich äquivalent...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ich verstehe immer noch nicht was deine Frage ist.

Versteht du was von C nicht oder willst du was über eine konkrete 
C-Implementation (avr-gcc) wissen?

von Mensch Müller (Gast)


Lesenswert?

Ich wundere mich darüber, dass gcc das nicht besser auf Codegröße 
optimiert.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Um darüber was zu lernen gibt's mehrere Möglichkeiten.

Zum einen Compiler-Dumps wie mit -fdump-ipa-all -fdump-tree-all 
-fdump-rtl-all.  Zum anderen kann man den Compiler (cc1) debuggen und 
nachvollziehen, wo er wenn warum welche Entscheidung (nicht) trifft. 
Schließlich gibt es noch Logs per -mlog=? und die Assembler-Ausgabe per 
-save-temps und -dP.  Und natürlich Hilfe von den GCC-Autoren in 
Mailin-Listen wie gcc-help@gcc.gnu.org siehe 
http://gcc.gnu.org/lists.html

Zudem denke ich, dass Compiler nie perfekt sein werden ebenso wie 
Menschen — und damit auch die Autoren von GCC — nicht perfekt sind.

Außerdem ist es nicht gewünscht dass Compiler den besten Code erzeugen: 
Den besten Code zu erzeugen impliziert nämlich NP-vollständige Probleme 
zu lösen, und um das zu bewerkstelligen würden Speicherplatz und 
Rechenzeit, die ein Compiler benötigt, um ein Programm zu übersetzen, 
exponentiell mit der größe des Programms steigen.

Weiters gibt es quasi niemanden, der ernsthaft daran interessiert ist, 
dass GCC besseren AVR-Code erzeugt — falls dem so wäre gäb es 
entsprechende Beitäge zu GCC-Entwicklung.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Mensch Müller schrieb:
> Ich wundere mich darüber, dass gcc das nicht besser auf Codegröße
> optimiert.

Zu deinem Beispiel mit der &&-Operation:

Zunächst einmal ist die Auswertereihenfolge in
1
expr1 && expr2

klar definiert, nämlich erst expr1 und dann expr2.

Wenn expr1 und expr2 beide ohne Nebeneffekte sind, dürfte der Compiler
prinzipiell ihre Auswertereihenfolge ändern, da dies am Ergebnis des
Gesamtausdrucks nichts ändert.

Oft möchte man aber gar nicht, dass der Compiler die Operanden
vertauscht, nur um 2 Bytes einzusparen. Ist bspw. expr1 ein Ausdruck,
der schnell auszuwerten und nur ganz selten wahr ist, sollte er auf
jeden Fall als erstes ausgewertet werden, da dadurch in den meisten
Fällen expr2 übersprungen werden kann.

Da nur der Programmierer – nicht aber der Compiler – die
Wahrscheinlichkeit von expr1 schätzen kann, ist es IMHO besser, der
Compiler überlässt die Wahl der Auswertereihenfolge dem Programmierer.

von Mensch Müller (Gast)


Lesenswert?

Ah, das ergibt Sinn. Lasse ich als Antwort gelten. Danke.

Aber das ließe sich ja über einen Optimierungsschalter lösen. ;) Ich 
verstehe natürlich, dass man nie "perfekten" Code erzeugen können wird. 
Aber hier hat man ja einen Ansatzpunkt für Verbesserungspotential. Ich 
bin die ganze Zeit davon ausgegangen, dass avr-gcc weiterentwickelt wird 
und man an solchen Ansätzen interessiert ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

avr-gcc ist Teil von GCC und profitiert damit natürlich auch von den 
Fortschritten an den maschinenunabhängigen Teilen.

Am AVR-Teil gibt's auch Änderungen, aber viele davon machen sich erst 
bemerkbar, wenn man sie auch (aktiv) nutzt.  Beispiele sind Support von 
ATtinyX, ATXmega, __flash, stdfix.h, Device-Specs und neue Attribute, 
die auch gerne mal Erweiterungen in libgcc, dem Build-System, Binutils 
und avr-libc erfordern.

Insgesamt würde ich den Traffic im AVR-Backend aber als gering 
bezeichnen; auf eine Skala von 0 bis 10 vielleicht auf 1 bis selten mal 
2, momentan tendierend zu 0.

Das größte Optimierungspotential bieten momentan IMO die 
avr-spezifischen Kostenfunktionen.  Die zu überarbeiten ist jedoch nicht 
einfach; es genügt ja nicht, ein Programm herzunehmen und anhand dessen 
die Kosten anzupassen.  Um sowas zu machen braucht's viel Zeit, Geduld & 
Spucke und auch einiges an Intuition und Erfahrung mit der Arbeitsweise 
von GCC.

von Mensch Müller (Gast)


Lesenswert?

Kommt man an alle diese Kostenfunktionen per Parameter dran? Um sie per 
Script schnell durchzuprobieren? Ich hatte mich mal irgendwann damit 
kurz beschäftigt, aber nur ein oder zwei gefunden, z.B. für Jumps.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Parametrisier ist -mbranch-cost und einiges geht über --param.  An auf 
RTX-Kosten wirkende Optionen gibt's -mmcu und -Os et al., aber das war's 
dann auch schon.  Die RTX-Kosten komplett zu parametrisieren wäre 
ziemlich sinnfrei.

Wenn man nicht weiß, was man da tut, hat man keine Chance.  Mit Kanonen 
schießt sich eben immer noch schlecht auf exponentielle Komplexität...

Für Kosten siehe (ca. 1000 Zeilen) 
http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr.c?revision=230016&view=markup#l9843

: 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.