mikrocontroller.net

Forum: Compiler & IDEs Codeoptimierung: Codesize bei mehrfachen Funktionsaufrufen


Autor: ---=DIAN=--- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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=---

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
---=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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
---=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

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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.

Autor: ---=DIAN=--- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: ---=DIAN=--- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also für die Argumente meine ich....

Autor: Uhu Uhuhu (uhu)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
---=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...

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
---=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

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: "loop unrolling" ist natürlich vor allem bei Optimierung auf 
Rechenzeit sinnvoll.

Oliver

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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"

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>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

Autor: ---=DIAN=--- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, wieder eineiges schlauer. Danke für eure Antworten...

---=DIAN=---

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.