Hallo,
der Titel ist sehr allgemein aber ich überlege mir inzwischen immer
öfter, ob es noch Sinn macht Code selbst zu optimieren.
Als Beispiel nenne ich mal die Vorberechnung eines Ausdrucks, der im
folgenden mehrfach wieder verwendet wird. Nimmt man lo und hi weg und
schreibt es direkt mehrfach rein werden 40 Bytes mehr erzeugt. Habe
eigentlich gedacht der GCC erkennt das und rechnet es vorher aus aber
dem ist nicht so. "Vorberechnung mehrfach verwendeter Ausdrücke" spart
immer ein, auch beim Z80.
Auch die (i=....) Geschichte lässt sich sicherlich optimieren, ein
funktionierender Ausdruck andderswo ist zb
SEG7.setDigit(display,segment+i,BCD[i], (i != k) ? 0:1);
wenn k die Kommapostion ist. Nur fiel mir hier jetzt nichts zu ein, wie
ichj die 6 Aufrufe opmtieren kann.
Beim STM32 mit 2 MB Flash, wovon mein Programm grad mal 40k nutzt +
fette 192k RAM fehlt mir dann auch das Verständnis dafür, ist doch
völlig wumpe, Platz en masse und bei 180 Mhz fällt die Mikrosekunde doch
kaum mehr auf. Beim Z80 schraube ich endlos rum, um "Assemblerhaufen" zu
shrinken aber beim STM32?
STM32: Da liegen bei mir inzwischzen 3 Schichten übereinander:
CMSIS ... scheint als wären das nur "Overlays", die keinen oder weniger
Code erzeugen. Finde ich ziemlich unverständlich beim Überfliegen des
Codes.
StdPeriphLib .... Kapselung diverser Hardware, "ladbare Structs" erzeugt
auf jeden Fall Code.
TilenMajeler_Lib..... weitergehende Kapselung der Hardware, basiert auf
StdPeriphLib. Macht aus dem STM32 dann auch endlich einen "Arduino",
überhaupt kein Kontakt mehr zur Hardware und Registern. Aber "optimal"
ist die auch nicht, die Grenzenprüfung beim Pixel setzen if (x> LIMIT)
jedesmal bremst bei meinen Millionen Pixelsetzern auch messbar aus,
daher entfernt.
Meine Frage: Optimiert ihr bei solchen Maschinen noch "lesbaren"
zugunsten von "optimalem Code", denn die Leserlichkeit nimmt bekanntlich
ab, wenn man zb manuell Schleifen aufrollt.
Die allgemein bekannten Optimierungen haben sich auch schon zu
Compilerbauern herumgesprochen. Die machen so was wie gemeinsame
Ausdrücke und Schleifenabrollen längst selber.
Fall sie den Namen Compilerbauern verdient haben.
Die anderen gibt es natürlich auch, aber deren Produkte muß man ja nicht
kaufen solange es sowas wie GCC gibt.
Bastler schrieb:> Die machen so was wie gemeinsame> Ausdrücke und Schleifenabrollen längst selber.
GCC aber nicht.... habe da viel rumprobiert. Ein Ausdruck wie
feld[x][y]
der nachfolgend 4 Mal verwendet wird kommt besser mit
x = feld[x][y]
und folgend mit x weiter.
Und noch besser wird es ohne [x][y], wenn die 2D Geschichte selbst
berechnet wird.
Christian J. schrieb:> der nachfolgend 4 Mal verwendet wird kommt besser mit>> x = feld[x][y]>> und folgend mit x weiter.
Was meiner Meinung nach auch lesbarer und damit besser ist (unter der
Annahme, dass man die Variable nicht "x", sondern sinnvoll benennt).
Prinzipiell schreibt man lesbaren und schönen Code. Der ist dann im
Normalfall auch schnell. Muss man wirklich einmal händisch optimieren,
dann nur dort wo das Problem ist.
Christian J. schrieb:> GCC aber nicht.... habe da viel rumprobiert. Ein Ausdruck wie>> feld[x][y]>> der nachfolgend 4 Mal verwendet wird kommt besser mit>> x = feld[x][y]>> und folgend mit x weiter.
das könnte daran liegen, dass der Compiler nicht in der Lage war,
nachzuweisen, dass es keinen Alias auf "feld" gibt. Wenn Du z.B. eine
Funktion aufrufst, dann muss der Compiler annehmen, dass Du "feld" ggf.
änderst.
Ich finde die zweite Version aber auch lesbarer, ansonsten würde ich
beim Lesen ja auf Alias-Suche gehen müssen ;-)
mfg Torsten
Christian J. schrieb:> Meine Frage: Optimiert ihr bei solchen Maschinen noch "lesbaren"> zugunsten von "optimalem Code", denn die Leserlichkeit nimmt bekanntlich> ab, wenn man zb manuell Schleifen aufrollt.
Da 90% des Codes weder zeitkritisch ist, noch oft die Codegröße eine
Rolle spielt: Zunächst optimiert man allen Code auf lesbarkeit. Wenn zu
langsam profilen, und die kritischen 10% optimieren.
Wobei lesbarkeit und Effizienz nicht immer ein Gegensatz sein muss:
Wenig, einfacher Code ist gut zu lesen und auch oft schnell.
Ausnahmen bestätigen die Regel: Wenn ich ein Produkt entwickle, das 1
Mio mal produiert wird, und ich durch Optimierung mit einem günstigeren
Chip auskomme, dann mag es sinnvoll sein, viel mehr Zeit in die
Optimierung zu stecken.
ZigZeg
Karl Heinz schrieb:> schreibst und kein 'else' benutzt, brauchen wir uns über weitere> Optimierungen nicht unterhalten.
warum? ist der Compiler so dumm um das nicht zu erkennen? i wird ja
nicht geändert, damit sollte nach der Optimierung das gleiche wie mit
else oder sogar als case rauskommen.
Peter II schrieb:> Karl Heinz schrieb:>> schreibst und kein 'else' benutzt, brauchen wir uns über weitere>> Optimierungen nicht unterhalten.>> warum?
weil er darauf achten soll, dem Compiler auch nicht sinnloserweise das
Leben schwer zu machen. Benutzt er else ist er nicht darauf angewiesen,
dass die Codeflussanalyse diesen Fall findet.
Obs schneller ist müsste man sich im Assembler ansehen. Ebenso, obs
kürzer ist. Aber so würde mir das erst mal reichen. Wichtiger wäre mir,
dass ich für 'i' einen anderen Variablennamen finde. i ist so
nichtssagend. 'callNr' oder 'displayPhase' oder irgendsowas in diese
Richtung wäre gut. 'i' sagt mir auf die schnelle gar nichts. 'i' heissen
traditionell global gesehen relativ unwichtige Indexvariablen in zb
for-Schleifen.
Karl Heinz schrieb:> weil er darauf achten soll, dem Compiler auch nicht sinnloserweise das> Leben schwer zu machen. Benutzt er else ist er nicht darauf angewiesen,> dass die Codeflussanalyse diesen Fall findet.
Mal ganz davon abgesehen, dass auch Menschen Code lesen und für die
macht es schon einen Unterschied, ob da
Karl Heinz schrieb:> weil er darauf achten soll, dem Compiler auch nicht sinnloserweise das> Leben schwer zu machen. Benutzt er else ist er nicht darauf angewiesen,> dass die Codeflussanalyse diesen Fall findet.
es wird doch immer behauptet man soll den code so schreiben das man ihn
gut lesen kann. Der Compiler macht das schon richtig und schnell. Und ob
es der Compiler nun einfach hat oder nicht kann mir als Entwickler doch
egal sein.
Wenn er so etwas nicht hinbekommt, dann kann ja schon fast wieder ASM
schreiben.
Peter II schrieb:> warum? ist der Compiler so dumm um das nicht zu erkennen?
Nein, ist er nicht. Er optimiert das. Mit und ohne "else" kommt der
gleiche Code raus.
Peter II schrieb:> Karl Heinz schrieb:>> weil er darauf achten soll, dem Compiler auch nicht sinnloserweise das>> Leben schwer zu machen. Benutzt er else ist er nicht darauf angewiesen,>> dass die Codeflussanalyse diesen Fall findet.>> es wird doch immer behauptet man soll den code so schreiben das man ihn> gut lesen kann.
Ich behaupte mal
1
if(i==0)
2
...
3
elseif(i==1)
4
...
5
elseif(i==2)
6
...
ist schneller und einfacher zu lesen als
1
if(i==0)
2
...
3
if(i==1)
4
...
5
if(i==2)
6
...
in der ersten Version muss ich nicht die ... Teile absuchen um zu
sehen, dass immer nur eines der 3 if zum Zuge kommen kann. Es ist durch
die if-else_if Leiter völlilg klar, dass ich eine einzige Auswahl aus
mehreren Möglichkeiten habe.
Peter II schrieb:> Karl Heinz schrieb:>> schreibst und kein 'else' benutzt, brauchen wir uns über weitere>> Optimierungen nicht unterhalten.>> warum? ist der Compiler so dumm um das nicht zu erkennen? i wird ja> nicht geändert, damit sollte nach der Optimierung das gleiche wie mit> else oder sogar als case rauskommen.
Karl Heinz schrieb:> Ich behaupte mal if( i == 0 )> ist schneller und einfacher zu lesen als
Ansichtssache, ich finde es ohne das else aufgeräumter und
übersichtlicher.
Peter II schrieb:>> Ich behaupte mal if( i == 0 )>> ist schneller und einfacher zu lesen als>> Ansichtssache, ich finde es ohne das else aufgeräumter und> übersichtlicher.
nachtrag:
so mal ich immer {} bei if und else verwende, das
Karl Heinz schrieb:> SEG7.setDigit( display, pos, lo, ( i == 2 ? 1 : 0 ) );> SEG7.setDigit( display, pos+1, hi, ( i == 1 ? 1 : 0 ) );
oder
1
SEG7.setDigit(display,pos,lo,(i>>1)&1);
2
SEG7.setDigit(display,pos+1,hi,i&1);
Je nachdem welche werte i überhaupt annehmen kann, welche Bedeutung es
hat, und was setDigit erwartet (boolean im sinne von 0 oder > 0) lässt
sich das noch weiter vereinfachen
Peter II schrieb:> Karl Heinz schrieb:>> schreibst und kein 'else' benutzt, brauchen wir uns über weitere>> Optimierungen nicht unterhalten.>> warum? ist der Compiler so dumm um das nicht zu erkennen? i wird ja> nicht geändert, damit sollte nach der Optimierung das gleiche wie mit> else oder sogar als case rauskommen.
Ein Leser des Codes könnte sich fragen, ob dem Autor nicht klar war,
dass nur einer von drei Fällen zutreffen kann. Und sich dann fragen,
warum der Autor dass dann nicht so ausgedrückt hat.
Die beiden Varianten kommunizieren schon etwas Unterschiedliches, auch
wenn der erzeugte Maschinencode evtl. der selbe ist.
Torsten Robitzki schrieb:> Ein Leser des Codes könnte sich fragen, ob dem Autor nicht klar war,> dass nur einer von drei Fällen zutreffen kann. Und sich dann fragen,> warum der Autor dass dann nicht so ausgedrückt hat.
dann verwendet man aber sinnvollerweise ein case.
Peter II schrieb:> Torsten Robitzki schrieb:>> Ein Leser des Codes könnte sich fragen, ob dem Autor nicht klar war,>> dass nur einer von drei Fällen zutreffen kann. Und sich dann fragen,>> warum der Autor dass dann nicht so ausgedrückt hat.>> dann verwendet man aber sinnvollerweise ein case.
Ist sicher auch eine Möglichkeit, auszudrücken, dass genau einer von n
Fällen auftreten kann. Bei n == 3 finde ich ein if/else-Konstrukt nicht
ungewöhnlich.
Peter II schrieb:> Torsten Robitzki schrieb:>> Ein Leser des Codes könnte sich fragen, ob dem Autor nicht klar war,>> dass nur einer von drei Fällen zutreffen kann. Und sich dann fragen,>> warum der Autor dass dann nicht so ausgedrückt hat.>> dann verwendet man aber sinnvollerweise ein case.
Das kommt drauf an.
1
if(adcWert<100){
2
..
3
}
4
elseif(adcWert<200){
5
..
6
}
7
elseif(adcWert<250){
8
..
9
}
10
else{
11
..
12
}
kannst du nicht durch ein switch-case ersetzen.
Eine if-else_if Leiter ist etwas ganz normales und hat ein feststehendes
'Idiom'. Eine von mehreren Auswahlen soll benutzt werden. Und dazu muss
ich mir die .. Teile nicht ansehen, ob dort vielleicht eine Bedingung
weiter unten manipuliert wird.
lese, dann ist die erste Frage die ich mir stelle: Wieso hat der Autor
kein "switch...case" genommen ?
Darauf gibt es zwei Antworten:
1. Es gibt/wird geben Vergleiche mit "<" und ">", dann hätte man aber
ein "if..else" genommen -> veworfen
2. In den if Paketen wird evtl. nochmal an i rumgeschrieben.
Das heißt ist muss jetzt den ganzen Code in den ifs lesen um sicher sein
zu können den ganzen Inhalt erfasst zu haben.
Das killt also Lesbarkeit und Optimierbarkeit (Wenn i z.B. volatile ist,
dann DARF der Compiler das garnicht optimieren, es könnte ja in einem
Interrupt geändert worden sein) in einem Schritt.
Noch besser wird es wenn, das i zu einer Statemachine gehört (dann ist i
aber ein schlechter name) und man im Nachhinein Schreibzugriffe auf i in
die ifs reinpackt, dann läuft das ganze nämlich Amok.
Die Moral von der Geschicht: Vergiss deine elses nicht.
>> Je nachdem welche werte i überhaupt annehmen kann, welche Bedeutung es> hat, und was setDigit erwartet (boolean im sinne von 0 oder > 0) lässt> sich das noch weiter vereinfachen
Das habe ich schon auprobiert und es kam was Falsches bei raus. Das
Komma wurde bei jeder Stelle gesetzt. Aber ich war zu müde da noch zu
suchen. Danke für den Hinweis, schaue heute abend mal nach.
else oder nicht ... es kommt immer bei das gleiche raus, habe ich auch
schon ausprobiert. case erzeugt manchmal längeren code.
Christian J. schrieb:> else oder nicht ... es kommt immer bei das gleiche raus, habe ich auch> schon ausprobiert. case erzeugt manchmal längeren code.
Mit welchen Compilerschaltern hast Du das den übersetzt?
Christian J. schrieb:> PS: i = Kommastelle einer Displayausgabe einer BCD Zahl, die getricksten> fix Koma float ausdrücken soll.
Dann ist 'i' aber ein ausgesprochen selten dämlicher Name für diesen
Parameter.
Karl Heinz schrieb:> Dann ist 'i' aber ein ausgesprochen selten dämlicher Name für diesen> Parameter.
Er ist ok, wenn man bedenkt, dass es 1 Uhr morgens war und ein Tuborg
gewirkt hatte....
>> kommaPos == 2
Cool. es gibt doch immer wieder was Neues zu lernen bei C. Werde es mal
ausprobieren, wenn ich wieder daheim bin.
Wo der Profi grad da ist (Karl-Heinz)....
Das System ist ein Atmega32PU in einer Lochraster, der über die Arduino
IDE mit ext. Editor bespielt wird. Ich nutze das nette Lib Konzept um
einfach lauffähigen Code für einige Sensoren und Displays zu erzeugen.
Die Chose hängt an der Wand und ist eine kleine Wetterstation mit
Tendenzanzeigen.
Dabei habe ich festgestellt, dass ich die Analog Eingänge nicht auf
Digital Out setzen kann. Es tut sich nichts, d.h. ich verschwende volle
7 Pins. Den Atmega32Pu habe ich eingebunden durch eine pin.h Datei und
eine boards.txt sowie diverse Codefragmente die jemand bereit stellt.
Ist das normal, dass man die Ax nicht als DigitalOut verwenden kann? Ich
meine der Atmega32 kann das doch.
Ist zwar etwas Off Topic aber meine Eingangsfrage wurde ja beantwortet.
Christian J. schrieb:> Ist zwar etwas Off Topic aber meine Eingangsfrage wurde ja beantwortet.
Dann solltest du trotzdem einen eigenen Thread aufmachen. Schon,
damit auch die sich ggf. dran beteiligen, für die eben dieser Thread
hier abgeschlossen ist und die nicht mehr reingucken.
Karl Heinz schrieb:> Solange du [...] schreibst und kein 'else' benutzt, brauchen> wir uns über weitere Optimierungen nicht unterhalten.
Das ist eine der einfachsten Übungen für den Compiler. i kann nicht
gleichzeitig 0 und 1 sein, etc.
Johann L. schrieb:> i kann nicht> gleichzeitig 0 und 1 sein
Der Tag, an dem gcc für einen Quantencomputer adaptiert wird, wird auch
noch kommen ;)
Oliver
Johann L. schrieb:> Karl Heinz schrieb:>> Solange du [...] schreibst und kein 'else' benutzt, brauchen>> wir uns über weitere Optimierungen nicht unterhalten.>> Das ist eine der einfachsten Übungen für den Compiler. i kann nicht> gleichzeitig 0 und 1 sein, etc.
Aber i kann sich in den vorhergehende if's ändern.
Die Varianten mit und ohne else sind nicht komplett äquivalent.
Der Compiler muss also analysieren, ob sich die if's wirklich
gebenseitig ausschließen.
Klaus Falser schrieb:> Der Compiler muss also analysieren, ob sich die if's wirklich> gebenseitig ausschließen.
Natürlich. Aber solche Analysen werden ohnehin bereits durchgeführt und
wirken auch anderswo auf die Optimierung. So ist das beispielsweise auch
erforderlich, um schleifeninvarianten Code zu erkennen.
http://en.wikipedia.org/wiki/Data-flow_analysis
Klaus Falser schrieb:> Johann L. schrieb:>> Karl Heinz schrieb:>>> Solange du [...] schreibst und kein 'else' benutzt, brauchen>>> wir uns über weitere Optimierungen nicht unterhalten.>>>> Das ist eine der einfachsten Übungen für den Compiler. i kann nicht>> gleichzeitig 0 und 1 sein, etc.>> Aber i kann sich in den vorhergehende if's ändern.
Nein, kann es nicht.
i ist eine auto Variable die in der ganzen gezeigten Sequenz nur als
rvalue, d.h. nur lesend verwendet wird, und auch die Adresse von i wird
nirgends genommen. Egal wie SEG7.setDigit() gestrickt ist, es kann
unmöglich i verändern.
Karl Heinz schrieb:> SEG7.setDigit( display, pos, lo, kommaPos == 2 );> SEG7.setDigit( display, pos+1, hi, kommaPos == 1 );
Nachsatz: Bringt 90 Bytes weniger Kot :-)
Wird auch mal zeit für eine Flasche Wein für den Karl-Heinz :-)
Inzwischen echt fällig...
Hallo,
ich papp das hier mal dran: Leider habe ich keinen asm Output und das
manuelle Erzeugen ist mir zu mühsam im Moment.
Ist der GCC cleer genug sehr kurze Funktionen als Inline zu realisieren?
Und wenn ja, muss dazu die -os Speed Optimierung gewählt werden oder
geht das auch mit der -o3? zb wenn die Funktion nur eine Zeile ist?
Christian J. schrieb:> Ist der GCC cleer genug sehr kurze Funktionen als Inline zu realisieren?
Ja.
> Und wenn ja, muss dazu die -os Speed Optimierung gewählt werden
Nein.
Peter II schrieb:> Karl Heinz schrieb:>> Ich behaupte mal if( i == 0 )>> ist schneller und einfacher zu lesen als>> Ansichtssache, ich finde es ohne das else aufgeräumter und> übersichtlicher.
Ich glaube, dann hast Du Dir abgewöhnt in die einzelnen Zweige zu
schauen, ob dort i verändert wird. Und genau dies macht die Variante
dann unübersichtlich.