Forum: Compiler & IDEs Frage zur Programmoptimierung WinAVR


von Harald P. (haraldp)


Lesenswert?

Per Zufall habe ich entdeckt, daß WinAVR 2 gleichwertige 
Codierungsmöglichkeiten völlig unterschiedlich übersetzt. Hier das 
Testbeispiel:

union hPuffer
{
 uint8_t Ges[12];
 struct
 {
  uint8_t Feld1[6];
  uint8_t Feld2[6];
 };
} Puffer;

uint8_t Erg[12];

void main (void)
{
 for (uint8_t i=0; i<6; i++)
 {
 Erg[i] = Puffer.Ges[6+i]; // Version a) 112Byte
// Erg[i] = Puffer.Feld2[i]; // Version b) 140Byte
 }

}

Bei Version a) wird auf den Puffer mit dem Offset 6 direkt zugegriffen,
bei Version b) muß der Compiler den Offset aus der union berechnet.
Die Ergebnisse sind total unterschiedlich:


Version a)
  56:  e6 e0         ldi  r30, 0x06  ; 6
  58:  f1 e0         ldi  r31, 0x01  ; 1
  5a:  ac e0         ldi  r26, 0x0C  ; 12
  5c:  b1 e0         ldi  r27, 0x01  ; 1
 for (uint8_t i=0; i<6; i++)
 {
 Erg[i] = Puffer.Ges[8+i]; // -> 112Byte
  5e:  81 91         ld  r24, Z+
  60:  8d 93         st  X+, r24

  62:  81 e0         ldi  r24, 0x01  ; 1
  64:  ec 30         cpi  r30, 0x0C  ; 12
  66:  f8 07         cpc  r31, r24
  68:  d1 f7         brne  .-12       ; 0x5e <main+0x8>

Version b)
 for (uint8_t i=0; i<6; i++)
 {
// Erg[i] = Puffer.Ges[8+i]; // -> 112Byte
 Erg[i] = Puffer.Feld2[i]; // -> 140Byte
  56:  80 91 06 01   lds  r24, 0x0106  ; -> Puffer+6
  5a:  80 93 0c 01   sts  0x010C, r24  ; -> Erg
  5e:  80 91 07 01   lds  r24, 0x0107  ; -> Puffer+7
  62:  80 93 0d 01   sts  0x010D, r24  ; -> Erg+1
  66:  80 91 08 01   lds  r24, 0x0108  ; -> Puffer+8
  6a:  80 93 0e 01   sts  0x010E, r24  ; -> Erg+2
  6e:  80 91 09 01   lds  r24, 0x0109  ; -> Puffer+9
  72:  80 93 0f 01   sts  0x010F, r24  ; -> Erg+3
  76:  80 91 0a 01   lds  r24, 0x010A  ; -> Puffer+10
  7a:  80 93 10 01   sts  0x0110, r24  ; -> Erg+4
  7e:  80 91 0b 01   lds  r24, 0x010B  ; -> Puffer+11
  82:  80 93 11 01   sts  0x0111, r24  ; -> Erg+5
 }

Getestet mit ATMEGA88 und WinAVR-20100110 mit folgenden Optionen:
## Compile options common for all C compilation units.
CFLAGS += -Wall -gdwarf-2  -Os  -DF_CPU=8000000UL
CFLAGS += -std=gnu99 -fno-inline-small-functions -Wno-main

Gibt es einen Grund, warum Version b) ineffizenter übersetzt wird - 
obwohl es eigentlicher eleganter ist - , und gibt es Regeln, damit 
solche Dinge vermieden werden können?

Harald

von hal9002 (Gast)


Lesenswert?

machs in Assembler

von Harald P. (haraldp)


Lesenswert?

.. eben nicht. Es geht nur in 2. Linie um effizienten Code. Mir ist 
wichtiger, daß das Programm übersichtlicher ist. Ein C-Programm ist nun 
'mal leichter verständlich als Assembler. Trotzdem wüßte ich in diesem 
Fall mehr über Hintergründe. Am besten ist natürlich leicht 
verständlicher (soweit mit C möglich) und kompakter Code.

von Tom M. (tomm) Benutzerseite


Lesenswert?

Harald P. schrieb:
> Gibt es einen Grund, warum Version b) ineffizenter übersetzt wird -

Ich vermute mal, dass der Compiler hier die Schleife ausrollt (unroll 
loop), weil er bei allen Indizes erkennt, dass sie konstant sind. 
Compilier doch mal mit -O2 und/oder mit -fno-unroll-loops, was macht der 
Compiler dann draus?

Warum das so ist - keine Ahnung, interessiert mich auch nicht 
wirklich(tm). ;o)

von Stefan E. (sternst)


Lesenswert?

Harald P. schrieb:
> Gibt es einen Grund, warum Version b) ineffizenter übersetzt wird -

Ineffizienter in welcher Hinsicht?
Version b ist deutlich schneller.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Harald P. schrieb:
> Gibt es einen Grund, warum Version b) ineffizenter übersetzt wird -
> obwohl es eigentlicher eleganter ist - ,

Das scheint ein "Feature" von GCC 4.4.x zu sein. GCC 4.5.4, 4.6.3
und 4.7.2 liefern für beide Fälle jeweils den gleichen Code: Mit -Os
oder -O2 wird beidesmal eine Schleife erzeugt, mit -O3 wird die Schleife
in sechs einzelne Kopieraktionen ungerollt, also genau so wie man es
erwartet (-O3 beeinhaltet -funroll-loops, -O2 und -Os nicht).

> und gibt es Regeln, damit solche Dinge vermieden werden können?

Date doch einfach mal den Compiler auf eine etwas jüngere Version up :)

von Peter D. (peda)


Lesenswert?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:

Ein Link zu avr-gcc 4.7.2 für Windows und eine kurze Beschreibung wird 
gegeben in fer avr-gcc Mailing-Liste [1]:

http://lists.gnu.org/archive/html/avr-gcc-list/2012-09/msg00024.html


[1]
https://lists.gnu.org/mailman/listinfo/avr-gcc-list

von Harald P. (haraldp)


Lesenswert?

Vielen Dank für eure Hilfe!
avr-gcc 4.7.2 kann ich jetzt benutzen, nachdem ich mein Programm 
umgestellt habe: prog_* entfernt, zusätzliche casts, um lästige 
Warnungen verschwinden zu lassen...
Seltsamerweise funktioniert das damit übersetzte Programm nicht mehr im 
Debugger des AVR-Studios V4.18. AVR-Studio stürtzt beim Laden ab 
(unbekannter SW-Fehler ..). Schade, mit dem Simulatur des Studios bin 
ich gut klar gekommen.

Harald

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Welches Debug-Format unterstützt dieser Debugger denn, und welches läst 
du erzeugen?

von Harald P. (haraldp)


Lesenswert?

Es sind die Standard-Optionen, die über Studio eingestellt werden:

## General Flags
PROJECT = AnzD640
MCU = atmega32
TARGET = AnzD640.elf
CC = avr-gcc

CPP = avr-g++

## Options common to compile, link and assembly rules
COMMON = -mmcu=$(MCU)

## Compile options common for all C compilation units.
CFLAGS = $(COMMON)
CFLAGS += -Wall -gdwarf-2 -std=gnu99 
-DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields 
-fpack-struct -fshort-enums
CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d

also dwarf-2. Wechsele ich den Compiler (geht ruck-zuck mittels 
junction-Befehl, wie hier früher mal beschrieben wurde), dann 
funktioniert das Debugging. Eben noch einmal probiert. Der Fehler ist 
reproduzierbar. Die Options sind gleich geblieben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Harald P. schrieb:

> CFLAGS += -Wall -gdwarf-2 -std=gnu99
> also dwarf-2.

Nö.  Dwarf-2 mit GNU-spezifischen Erweiterungen. Dwarf-2 ist -gdwarf-2 
-gstrict-dwarf, was hier vermutlich nicht hilft, denn...

> Wechsele ich den Compiler [...] dann funktioniert das Debugging.

...AFAIK scannt der Atmel-Debugger Instruktionssequenzen anstatt 
Debug-Information zum Erkennung von CFI-Info etc. zu bekommen.

Heisst konkret:  Sobald der Compiler auch nur ein einzige unerwartete 
Instruktion ausgibt oder die Abfolge der Instruktionen ändert, ist 
dieser Debugger verwirrt; egal wie vollständig oder korrekt die erzeugte 
Debug-Info des Compilers auch sein mag.

Evtl. einen Debugger verwenden, der tatsächlich in der Lage ist, 
Debug-Informationen auszuwerten anstatt hartcodierte 
Instruktionssequenzen abzuscannen und bei der kleinsten Abweichung 
abschmiert.

Bei der Entwicklung der Tools (GCC, Binutils, GDB, ...) geht es darum, 
die Tools weiter zu entwickeln.  Wenn ein Debugger-Hersteller da 
irgendwelche Hacks in seine Software einbaut, die nirgends spezifiziert 
sind, und damit riskiert, daß seine Software bei der kleinsten Änderung 
nicht mehr tut — sorry, ich werd mich um solche Sonderlocken nicht 
kümmern, und ich wüsste auch keinen anderen GCC-Entwickler, der das tut 
oder unterstützen würde.

Falls der Atmel-Debugger sich einzig auf Debug-Info verlässt und keine 
solchen Scans gegen bestimmte Instruktionssequenzen veranstaltet, lasse 
ich mich gerne eines Besseren belehren.

von Harald P. (haraldp)


Lesenswert?

.. perfekt. Die Option -gstrict-dwarf (wohl nur erlaubt für die neueren 
Compiler-Versionen) sorgt dafür, daß der Atmel-Debugger nicht mehr 
abstürzt. Vielen Dank an die Spezialisten, die hier im Forum 
mitarbeiten.
PS: in der Version 4.7.2 (mit mingw) kennt avr-size nicht die für AVR 
nützliche Option C. Man kann aber einfach ein älteres avr-size in das 
entsprechende Verzeichnis kopieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Harald P. schrieb:
> .. perfekt. Die Option -gstrict-dwarf (wohl nur erlaubt für die neueren
> Compiler-Versionen) sorgt dafür, daß der Atmel-Debugger nicht mehr
> abstürzt.

Diese Option gibt es in allen supporteten Versionen von GCC und sogar 
schon in der 4.5, d.h. seit 2 1/2 Jahren.

Schafft der Debugger auch dwarf-3 oder gar dwarf-4?

> in der Version 4.7.2 (mit mingw) kennt avr-size nicht die für AVR
> nützliche Option C.

Falls es um die o.g. 
http://lists.gnu.org/archive/html/avr-gcc-list/2012-09/msg00024.html 
geht: das steht im Fineprint:

>> The package contains no extra patches [...]

von Harald P. (haraldp)


Lesenswert?

Mit dwarf-3 (mit oder ohne -gstrict-dwarf) läuft der Debugger, eben 
ausprobiert. Aber das Debugging geht nicht auf Source-Code Ebene, ist 
also (fast) unbrauchbar. Mit dwarf-4 (mit oder ohne -gstrict-dwarf) 
stürzt der Debugger ab.

von Knut S. (kschwi)


Lesenswert?

Hallo zusammen,

da ich gerade beim Debuggen mit AS6 ICE3 & MKII eine Verzweifelung nach 
der nächsten bekommen schaue ich mir gerade die Alternativen an. Ich 
habe versucht herauszubekommen, was DARF-2, 3 und 4 unterscheidet. Von 
letzterem habe ich nur gelesen, dass man GDB 7.0 benötigt. Kennt 
irgendwer Details?
Ciao
Knut

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.