Forum: Compiler & IDEs Verwendung von Structs langsamer?


von peterguy (Gast)


Lesenswert?

Hallo Leute,

wir haben hier eine Diskussion am Laufen, ob es den Code langsamer 
macht, wenn man structs anstelle von "normalen Variablen" verwendet.

Hat das mal jemand ausgemessen, bzw. den assembler code sich angeschaut?

von Karl H. (kbuchegg)


Lesenswert?

peterguy schrieb:
> Hallo Leute,
>
> wir haben hier eine Diskussion am Laufen, ob es den Code langsamer
> macht, wenn man structs anstelle von "normalen Variablen" verwendet.
>
> Hat das mal jemand ausgemessen, bzw. den assembler code sich angeschaut?

Warum macht ihr das nicht einfach?

Erwarten würde ich, dass sich beide Varianten nichts schenken, eher im 
Gegenteil, dass die struct Variante bei vernünftiger Programmierung 
schneller ist.

von Mark B. (markbrandis)


Lesenswert?

Ich tippe mal auf gleich schnell. Im Endeffekt steht doch jede Variable 
an einer bestimmten Stelle im Speicher. Zum Verarbeiten muss sie erst in 
ein Register geladen werden usw.
Von daher wüsste ich jetzt nicht, warum es einen Unterschied machen 
sollte? Es sei denn vielleicht dass bei einer struct die Variablen 
direkt hintereinander im Speicher liegen und nicht "wild verstreut", und 
dass der Prozessor auf aufeinanderfolgende Speicherstellen schneller 
zugreifen könnte...

von Karl H. (kbuchegg)


Lesenswert?

Mark Brandis schrieb:

> sollte? Es sei denn vielleicht dass bei einer struct die Variablen
> direkt hintereinander im Speicher liegen und nicht "wild verstreut", und
> dass der Prozessor auf aufeinanderfolgende Speicherstellen schneller
> zugreifen könnte...

Das ist auch meine Überlegung, dass der Compiler dann mit einem 
Basisregister und Offset arbeiten kann anstatt ständig irgendwelche 
Adressregister mit Werten laden zu müssen. Auch sollten Funktionsaufrufe 
einen Tick schneller gehen, wenn anstelle von 5 Variablen nur eine 
Basisadresse einer struct übergeben werden muss.

Aber wie heißt es so schön: Versuch macht kluch.

von Peter (Gast)


Lesenswert?

Ich glaube auf einem Atmel ist zu lesende zugriff auf eine struct member 
langsamber.

Erst muss er die adresse vom Struct lesen und dann darauf den offset 
addieren (ich glaube nicht das er es optimieren kann).

Aber bei der übergaben an eine Funktion sollte eine struct schneller 
sein, weil er ja nur eine Adresse übergeben muss, statt mehre Variablen.

Es wird wohl abhängig vom Programm sein, mal ist das eine mal das andere 
schneller.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

x = abcd;

ist ebenso schnell wie

x = a.b.c.d;

Weil der Compiler alle Offsets ausrechnen kann und keine zusätzlichen 
Befehle zur Laufzeit notwendig sind

Falls es nicht gecshachtelte Strukturen/Unions sind sondern Zeiger, 
müssen natürlich (idR) Indirektionen zur Laufzeit ausgeführt werden, 
also

x = a->b->c->d;

Strukturen können ja nach Anwendungsfall Vor- oder Nachteile bieten.

Beispiel 1
avr-gcc (sizeof(int) = 2)

Es ist eine Funktion mit 4 char-Werten aufzurufen:
1
void foo (char,char,char,char);

Die Werte werden dann in den Registern R24, R22, R20, R18 übergeben und 
R25,R23,R21,R19 bleiben ungenutzt. Mit Struktur
1
struct char2 {char a, b;};
2
struct char4 {char a, b, c, d;};
3
4
void foo2 (struct char2, struct char2);
5
void foo4 (struct char4);

wird die Struktur(en) in den Registern R22,R23,R24,R25 übergeben. Das 
bedeutet eine bessere Registerausnutzung.

Sollen mehrere Werte zurückgegeben werden, ist dies je nach ABI auch 
dann innerhalb eines Registers möglich, wenn eine Struktur zurückzugeben 
ist.

Sind zB 4 Chars zurückzugeben, dann geht das effizient mit struct char4, 
während die Standard-Lösung über Zeiger der Overkill ist:
1
void foo8_char (char,char,char,char,char*,char*,char*,char*);
2
3
struct char4 foo8_char4 (struct char4);

Die zweite Variante ist um Längen effizienter.

Weiterhin eröffnen Strukturen die Möglichkeut, effizienter auf Daten 
zuzugreifen, denn in einer Struktur sind alle Elemente definiert 
angeordnet und haben bekannte Offsets zum Strukturanfang, was bei 
Variablen nicht so ist. Deren Adressen haben keine dem Compiler bekannte 
Beziehung und werden erst zur Linkzeit festgelegt:
1
int a, b, c, d;
2
struct abcd { int a, b, c, d; };
3
4
void add1 (void)
5
{
6
    a++;
7
    b++;
8
    c++;
9
    d++;
10
};
11
12
void add2 (struct abcd * s)
13
{
14
    s->a ++;
15
    s->b ++;
16
    s->c ++;
17
    s->d ++;
18
}

Schaut man sie hier die Codegröße für an (avr-gcc) dann belegt add1 76 
Bytes, wohingegen add2 mit 46 auskommt und nur 2 Ticks langsamer ist. 
(Gesamtdauer ca. 26 Ticks).

Johann

von dummi (Gast)


Lesenswert?

>Aber bei der übergaben an eine Funktion sollte eine struct schneller
>sein, weil er ja nur eine Adresse übergeben muss, statt mehre Variablen.


Aber nur bei der Übergabe, beim Zugriff muss dann über Offset 
zugegriffen werden, da kann sich das bei vielen zugriffen rächen.

Ist aber alles sehr Architektur und Compiler abhängig.


Also Compileroutput begutachten.

mfg.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter schrieb:

> Erst muss er die adresse vom Struct lesen und dann darauf den offset
> addieren (ich glaube nicht das er es optimieren kann).

Hier mal ein Beispiel
1
int a, b, c, d;
2
struct s { int a, b, c, d[10]; struct {int e, d, f, g[5];} s; } s;
3
4
int get_a (void)
5
{
6
    return a;
7
}
8
9
int get_s_a (void)
10
{
11
    return s.a;
12
}
13
14
int get_s_d5 (void)
15
{
16
    return s.d[5];
17
}
18
19
int get_s_s_g4 (void)
20
{
21
    return s.s.g[4];
22
}
23
24
int get_pa (int *a)
25
{
26
    return *a;
27
}
28
29
int get_ps_a (struct s * s)
30
{
31
    return s->a;
32
}
33
34
int get_ps_d5 (struct s * s)
35
{
36
    return s->d[5];
37
}
38
39
int get_ps_s_g4 (struct s * s)
40
{
41
    return s->s.g[4];
42
}

Assembler
1
get_a:
2
  lds r24,a
3
  lds r25,(a)+1
4
  ret
5
6
get_s_a:
7
  lds r24,s
8
  lds r25,(s)+1
9
  ret
10
11
get_s_d5:
12
  lds r24,s+16
13
  lds r25,(s+16)+1
14
  ret
15
16
get_s_s_g4:
17
  lds r24,s+40
18
  lds r25,(s+40)+1
19
  ret
20
21
get_pa:
22
  movw r30,r24
23
  ld r24,Z
24
  ldd r25,Z+1
25
  ret
26
27
get_ps_a:
28
  movw r30,r24
29
  ld r24,Z
30
  ldd r25,Z+1
31
  ret
32
33
get_ps_d5:
34
  movw r30,r24
35
  ldd r24,Z+16
36
  ldd r25,Z+17
37
  ret
38
39
get_ps_s_g4:
40
  movw r30,r24
41
  ldd r24,Z+40
42
  ldd r25,Z+41
43
  ret

Wie man sieht, wird da nix unnötig zur Laufzeit berechnet.

Die nötige Arithmetik wird -- falls möglich -- aufgehteilt zwischen 
Compiler, Assembler und Linker.

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

dummi schrieb:
>>Aber bei der übergaben an eine Funktion sollte eine struct schneller
>>sein, weil er ja nur eine Adresse übergeben muss, statt mehre Variablen.
>
>
> Aber nur bei der Übergabe, beim Zugriff muss dann über Offset
> zugegriffen werden, da kann sich das bei vielen zugriffen rächen.

Nö. Wie machst du es denn mit 10 Zugriffen wenn die Variablen im 
Speicher verstreut sind? 10 Adressen übergeben?

Johann

von Thomas W. (wagneth)


Lesenswert?

Gab es nicht genau deswegen solche Compilerschalter wie WordAlignment ?

Bei denen "leerblöcke" mit eingabaut würden um den Zugriff zu 
beschleunigen,
bzw weggelassen um Platz zu sparen ?!

von Mark B. (markbrandis)


Lesenswert?

Bei einem 8-Bit-Prozessor mit 8-Bit-Datenbus (Atmel AVR) wohl eher nicht 
;-)

von Peter (Gast)


Lesenswert?

@Mark Brandis
ist nicht der EEProm oder der Flash 16 Alignt - zumindest die 
Adressierung

von Mark B. (markbrandis)


Lesenswert?

Beim Flash glaube ja, ich meinte jetzt auch eher den Hauptspeicher in 
dem zur Laufzeit die Variablen herum(f)liegen.

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.