Hallo, ich hätte da mal ne Frage zur Optimierung. ich habe eine "Funktion" die ein Bitmapmuster ans Grafikdisplay schickt: void Lcd_Write(unsigned char Bytewert,unsigned char Daten) { if (Daten==0) {LCD_A0_PORT&=~(1<<LCD_A0_BIT);} else {LCD_A0_PORT|=(1<<LCD_A0_BIT);} LCD_WR_PORT|=(1<<LCD_WR_BIT); LCD_DATEN_PORT=Bytewert; LCD_WR_PORT&=~(1<<LCD_WR_BIT); LCD_WR_PORT|=(1<<LCD_WR_BIT); } soweit so gut. (Funktioniert auch ;-) Nun muß ich für einen 12X16 Font diese "Funktion" mehrmals aufrufen z.B. so: Lcd_Write(pgm_read_word(&x_font[Zeichen][0]),1,0); Lcd_Write(pgm_read_word(&x_font[Zeichen][2]),1,0); Lcd_Write(pgm_read_word(&x_font[Zeichen][4]),1,0); Lcd_Write(pgm_read_word(&x_font[Zeichen][6]),1,0); Lcd_Write(pgm_read_word(&x_font[Zeichen][8]),1,0); Lcd_Write(pgm_read_word(&x_font[Zeichen][10]),1,0); nun ist mir aufgefallen, das pro Aufruf der Funktion meine Codesize um 12 Bytes wächst, was genau der Größe dieser Funktion entspricht. (ich kann auch mehrmals den Code der Funktion ausführen...) Ich dachte immer, wenn das ganze in Assembler umgesetzt wird, die Funktion z.B. an Adresse 0xirgentwas steht und wenn sie aus dem Hauptprogramm aufgerufen wird macht der Assembler dann sowas wie: Lade Register mit Argument 1; Lade Register mit Argument 2; Speichere Rücksprungadresse auf dem Stack; Jump zur Funktion an Adresse 0xirgentwas; Mache ich da was falsch? Makefile Falsch eingestellt? ist das immer so und nicht zu ändern? Danke ---=DIAN=---
---=DIAN=--- wrote: > soweit so gut. (Funktioniert auch ;-) > Nun muß ich für einen 12X16 Font diese "Funktion" mehrmals aufrufen z.B. > so: > > Lcd_Write(pgm_read_word(&x_font[Zeichen][0]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][2]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][4]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][6]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][8]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][10]),1,0); > > nun ist mir aufgefallen, das pro Aufruf der Funktion meine Codesize um > 12 Bytes wächst, was genau der Größe dieser Funktion entspricht. (ich > kann auch mehrmals den Code der Funktion ausführen...) Deine Funktion Lcd_Write ist mit Sicherheit länger als 12 Byte. > Ich dachte immer, wenn das ganze in Assembler umgesetzt wird, die > Funktion z.B. an Adresse 0xirgentwas steht und wenn sie aus dem > Hauptprogramm aufgerufen wird macht der Assembler dann sowas wie: > > Lade Register mit Argument 1; > Lade Register mit Argument 2; > Speichere Rücksprungadresse auf dem Stack; > Jump zur Funktion an Adresse 0xirgentwas; Jump geht nicht, es muß schon ein call sein, sonst findet er nicht mehr zurück. Der Code wird nur dann physikalisch an Stelle des Aufrufes einkopiert, wenn Lcd_Write keine Funktion, sondern ein Macro ist, oder wenn Lcd_Write eine inline-Funktion ist.
---=DIAN=--- wrote: > Lcd_Write(pgm_read_word(&x_font[Zeichen][0]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][2]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][4]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][6]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][8]),1,0); > Lcd_Write(pgm_read_word(&x_font[Zeichen][10]),1,0); 2 Argumente sind konstant und eines steigt in 2-er Schritten, das schreit doch geradezu nach einer Schleife. Peter
>Lade Register mit Argument 1; >Lade Register mit Argument 2; >Speichere Rücksprungadresse auf dem Stack; >Jump zur Funktion an Adresse 0xirgentwas; Lcd_Write(pgm_read_word(&x_font[Zeichen][0]),1,0); 12 Bytes pro Aufruf ist doch ok. Nimm das mal auseinander: pgm_read_word(&x_font[Zeichen][0]) Das ist schon ein Funktionsaufruf mit Parametern auf ein immerhin zweidimensionales Array. Das Ergebnis von pgm_read_word() wird widerum als Parameter für Lcd_Write() benutzt, und dann kommen noch zwei Parameter für die Position. Mit 12 Bytes pro Aufruf von Lcd_Write() bist du erstklassig bedient.
Also erstmal Danke für die Antworten, dass mit ner Schleife ist mir schon klar, aber ich hätte gedacht das die OPTIMIERUNG daraus ne Schleife machen kann!? Sie kann ja auch nutzlosen Code entfernen (hab ich mal in nem Tread gelesen wo es um eigene Delays ging...) Es ging mir ja um das Generelle. Wenn jetzt ___z.B.___ die Routine Lcd_Write mehr Code beinhaltet sagen wir mal 100 Bytes, und die Argumente nicht mit ner Formel berechnet werden können (z.B. 1,9,3,7,11,17,13,27) was dann? Nen Array nehmen?
---=DIAN=--- wrote: > Also erstmal Danke für die Antworten, dass mit ner Schleife ist mir > schon klar, aber ich hätte gedacht das die OPTIMIERUNG daraus ne > Schleife machen kann!? Sie kann ja auch nutzlosen Code entfernen (hab > ich mal in nem Tread gelesen wo es um eigene Delays ging...) Der Compiler ist zwar schlau, aber nicht allwissend. Um Konflikte zu vermeiden geht er bei Dingen, die er nicht versteht, davon aus, daß der Programmierer weiß, was er tut. Wenn du mal richtig fit in C bist, würdest du dich bedanken, wenn er nach Gutdünken irgendwas aus deinem Code machte...
---=DIAN=--- wrote: > Argumente nicht mit ner Formel berechnet werden können (z.B. > 1,9,3,7,11,17,13,27) was dann? Nen Array nehmen? Ganz genau. Eine Tabelle kostet ja nur den Tabelleneintrag, ist also sehr codeeffizient. Peter
>Lcd_Write mehr Code beinhaltet sagen wir mal 100 Bytes, und die >Argumente nicht mit ner Formel berechnet werden können (z.B. >1,9,3,7,11,17,13,27) was dann? Nix was dann. Der Aufruf von Lcd_Write() kostet dich immer nur noch 12 Byte. Egal wie groß Lcd_Write() ist.
>Lcd_Write mehr Code beinhaltet sagen wir mal 100 Bytes, und die >Argumente nicht mit ner Formel berechnet werden können (z.B. >1,9,3,7,11,17,13,27) was dann? Solange die Arrayindizes konstant sind, berechnte der Compiler die Elementadresse schon zur Compilezeit. Die Rechenzeit bleibt daher immer gleich. >aber ich hätte gedacht das die OPTIMIERUNG daraus ne >Schleife machen kann!? Was wäre denn der Vorteil einer Schleife? Da muß dann für jeden Funktionsaufruf die Schleifenvariable erhöht und auf Abbruch geprüft werden, und die in Berechnung der Arrayelelementadressen, die in der alten Version schon zur Compilezeit durchgeführt wird, kommt auch noch dazu. Alles in allem deutlich länger. Daher gibt es als Optimierung den umgekehrten Schritt: Schleifen mit zur Compilezeit bekannter Durchlaufzahl können vom Compiler in lineare Einzelaufrufe umgewandelt werden. ("loop unrolling"). Oliver
Nachtrag: "loop unrolling" ist natürlich vor allem bei Optimierung auf Rechenzeit sinnvoll. Oliver
Oliver wrote: > Was wäre denn der Vorteil einer Schleife? Da muß dann für jeden > Funktionsaufruf die Schleifenvariable erhöht und auf Abbruch geprüft > werden, und die in Berechnung der Arrayelelementadressen, die in der > alten Version schon zur Compilezeit durchgeführt wird, kommt auch noch > dazu. Alles in allem deutlich länger. Was meinst du mit länger: Laufzeit oder Code? Falls Code: Das wage ich im konkreten Fall mal zu bezweifeln. In 12 Bytes für einen Funktionsaufruf kann man leicht die ganze Schleifensteuerung samt Adressberechnung unterbringen. Spätestens nach dem 2. Funktions- aufruf dürfte die Schleifenvariante schon deutlich kürzer sein. Falls Laufzeit: Die entrollte Version wird sicherlich etwas schneller sein. Ist im vorliegenden konkreten Fall ein inc und ein cmp. Dazu eventuell noch ein push und ein pop um die Schleifenvariable vor der Funktion zu schützen und das wars mit der Schleifensteuerung. Die Adressberechnung, da sie schön linear geht, wird der Compiler mit einem Pointer machen, den er in der Schleife um immer einen konstanten Betrag erhöht. Summa summarum ergibt sich natürlich ein Overhead und man muss entscheiden was einem wichtiger ist: Laufzeit oder Codegrösse. Allerdings: Heutige Compiler machen das Loop-unrolling ganz von alleine, wenn man auf Laufzeit optimieren lässt. Im Zweifelsfall schreibt man im Allgemeinen den Code so wie er am klarsten ist und überlässt solche 'Low-Level' Optimierungen dem Compiler. Wie sagte schon Donald E. Knuth "Premature optimization is the root of all evil" "Vorzeitige Optimierung ist die Wurzel allen Übels"
>Was meinst du mit länger: Laufzeit oder Code? Daher mein Nachtrag oben. Länger = Langsamer. >Im Zweifelsfall schreibt man im Allgemeinen den Code so wie er >am klarsten ist und überlässt solche 'Low-Level' Optimierungen >dem Compiler. Volle Zustimmung. Oliver
>dem Compiler. Wie sagte schon Donald E. Knuth >"Premature optimization is the root of all evil" Falsch, die Wahrheit ist viel schlimmer. ;-) http://www.msxnet.org/humour/girls-are-evil.jpg MFG Falk
Ok, wieder eineiges schlauer. Danke für eure Antworten... ---=DIAN=---
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.