Forum: Compiler & IDEs Programmgröße optimieren, globale Variablen in struct?


von ben (Gast)


Lesenswert?

Hallo, ich versuche gerade die Programmgröße eines größeren Projektes zu 
optimieren, dessen Umfang langsam an die Grenze des Mega8 kommt.

Ich habe mir die application note AVR035: Efficient C Coding for AVR 
http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf 
durchgelesen und dort heißt es unter Anderem: "Collect non-local data in 
structures whenever natural. This increases the possibility
of indirect addressing without pointer reload."

was mir nicht ganz klar ist: gilt das nur für einzelne Variablen, wie 
z.B. Flags oder Zähler, oder macht es auch Sinn größere Speicherbereiche 
in structs zu packen?

Ich habe testweise Datenbereiche, die global liegen müssen in eine 
struct gepackt, das Ergebnis war, dass der code deutlich größer 
(~400Byte) war als vorher. Das sah in etwa so aus:
1
//global in einer header datei
2
typedef struct{
3
unsigned char var1[32];
4
unsigned char var2[64];
5
}daten;
6
7
daten a;
8
9
//irgendwo
10
a.var1[3] = 134;
11
/usw...
Wieso wird hier plötzlich soviel mehr Code erzeugt? Habe ich etwas 
übersehen?

von Karl H. (kbuchegg)


Lesenswert?

ben wrote:
>
> Ich habe testweise Datenbereiche, die global liegen müssen in eine
> struct gepackt, das Ergebnis war, dass der code deutlich größer
> (~400Byte) war als vorher. Das sah in etwa so aus:
>
1
> //global in einer header datei
2
> typedef struct{
3
> unsigned char var1[32];
4
> unsigned char var2[64];
5
> }daten;
6
> 
7
> daten a;
8
> 
9
> //irgendwo
10
> a.var1[3] = 134;
11
> /usw...
12
>

Das wird dir nichgt viel bringen (wenn überhaupt)

Wovon die Appnote spricht, ist sowas:

Anstelle von
1
uint8_t day;
2
uint8_t month;
3
uint8_t year;

wird vorgeschlagen, das so zu machen
1
struct date
2
{
3
  uint8_t day;
4
  uint8_t month;
5
  uint8_t year;
6
};
7
8
struct date d;

Die angegebene Begründung folgt folgender Idee:
Wenn du ein Datum verändern muss, dann müssen nacheinander
day, month, year aus dem Speicher geladen (oder geschrieben)
werden. Dazu muss aber die Speicheradresse gebildet werden,
damit mit einem Load bzw. Store darauf zugegriffen werden
kann. Da es sich um 3 getrennte Variablen handelt, wird die
Adresse mglw. 3 mal in einem Register zusammengebaut um die
Operation über die Bühne gehen zu lassen.
In der Strukturvariante hingegen, genügt es die Speicheradresse
nur einmal zu bestimmen um dann alle 3 Operationen relativ
zu dieser Adresse durchzuführen.

Ob dieses Argument heute noch zieht: Keine Ahnung. Schreib
so, wie es dir am Natürlichsten vorkommt. Wenn einzelne Variablen
logisch zusammengehören, so wie im obigen Beispiel ein Datum,
dann verpass dem Ganzen eine Struktur. Dadurch wird dann oft
die Übergabe solcher Daten an Funktionen vereinfacht.
Wenn die Daten nichts miteinander zu tun haben, dann lass es sein.

> Wieso wird hier plötzlich soviel mehr Code erzeugt? Habe ich etwas
> übersehen?

Man müsste sich den Code genau ansehen und was der Compiler
daraus macht. Allerdings ist eine Erhöhung von 400 byte schon
keine Kleinigkeit mehr. Daher war diese Änderung kontraproduktiv.
So ist das bei Optimierungen: Manchmal bewirken gut gemeinte
Optimierungen das genaue Gegenteil.

Gerade für Optimierungen gibt es kein Patentrezepz.

von Peter D. (peda)


Lesenswert?

ben wrote:
> Hallo, ich versuche gerade die Programmgröße eines größeren Projektes zu
> optimieren, dessen Umfang langsam an die Grenze des Mega8 kommt.

Ehe Du anfängst, an irgendner Schraube zu drehen, mußt Du erstmal 
wissen, wer die Speicherfresser sind.

Also laß Dir mal das MAP-File und das LST-File mit den Quelltextzeilen 
erzeugen.

Im MAP stehen die Speichergrößen für jede Funktion.
Die Funktionen mit den größten Werten mal genauer ansehen.

Im LST sieht man sehr schön, wieviel Code jede einzelne C-Zeile erzeugt.
Schön sind Zeilen, die nur 1..2 Assemblerbefehle erzeugen.
Es können aber auch 100 Assemblerbefehle sein, dann könnte 
Optimierungspotential bestehen.


Oftmals sind große Optimierungen durch Aufräumen von Spaghetticode 
möglich.
D.h. viele ähnliche Codeabschnitte durch eine Unterfunktion ersetzen und 
diese dann mit den entsprechenden Parametern aufrufen.


Peter

von Philipp B. (philipp_burch)


Lesenswert?

Peter Dannegger wrote:
> Oftmals sind große Optimierungen durch Aufräumen von Spaghetticode
> möglich.
> D.h. viele ähnliche Codeabschnitte durch eine Unterfunktion ersetzen und
> diese dann mit den entsprechenden Parametern aufrufen.

Ausserdem sollten grosse/grössere if/switch-Blöcke vermieden werden 
(Sprünge sind manchmal ziemlich teuer). Wenn immer möglich, so viel 
ähnliche Funktionalität wie möglich in eine Tabelle packen und nur noch 
einmal mit dem entsprechenden Tabellenwert erzeugen lassen.
Bsp:
1
uint8_t x;
2
//...
3
switch(x) {
4
  case 0: PORTA = 0x05; break;
5
  case 1: PORTA = 0x06; PORTB |= 0x01; break;
6
  case 2: PORTA = 0x80; break;
7
  case 3: PORTA = 0x00; PORTB |= 0x01; break;
8
  //...
9
}
10
11
//Ändern in
12
uint8_t x;
13
//...
14
const uint8_t table[] PROGMEM = {0x05, 0x06, 0x80, 0x00 /* , ... */};
15
if (x < 4) {
16
  PORTA = pgm_read_byte(&table[x]);
17
  if (x % 2) {   //% 2 wird hoffentlich in & 1 umgesetzt...
18
    PORTB |= 0x01;
19
  }
20
}

Oder so irgendwie...

inline-ifs würde ich übrigens auch vermeiden, die scheint der GCC gerne 
in 16 Bit-Operationen umzusetzen (x = y < 3 ? 4 : 2).

von daniel (Gast)


Lesenswert?

du bindest doch diese eine global.h nicht in allen .c dateien?
dann hast du doch den salat in jeder datei.
extern daten a; // <<

grüsse, daniel

von ben (Gast)


Lesenswert?

@Peter Dannegger
danke, ich werde mir die files mal anschauen

@Philipp Burch
Sowas kommt bei mir nicht vor, aber danke für den Tip, werde ich mir 
merken.

@daniel
hatte tatsächlich die deklaration in der h datei.
habe das jetzt mal geändert, d.h. alle deklarationen in ein c file und 
im h file dann mit extern. Die Dateigröße hat sich dadurch allerding 
überhaupt nicht geändert.
Muss dazu sagen, dass ich die variablen alle volatile deklariert habe, 
da auf sie aus verschiedenen interrupts zugeriffen werden muss..

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.