Forum: Compiler & IDEs GCC, Word statt Byte Zugriffe, Optimierung?


von thkais (Gast)


Lesenswert?

Erst mal sorry für den komplizierten Betreff, aber mir fiel nix besseres
ein.
Ich erleichtere mir oft das Layout, indem ich die Port-Pins nach
gutdünken verteile und nachher in Software entsprechend darauf
zugreife. Das funktioniert sowohl in C als auch in Assembler bestens
(solange man natürlich nichts zeitkritisches vor hat).
Mir ist nun beim GCC aufgefallen, daß bei der Umsetzung unnötigerweise
einige Byte-Operationen als Word ausgeführt werden. Zum Beispiel:

void set_outputs(uint8_t data)
{
  PORTC = (PORTC & 0xF7) | ((data & 0x01)<<3);
  PORTD = (PORTD & 0x03) | ((data & 0x7E)<<1);
  PORTB = (PORTB & 0xFE) | ((data & 0x80)>>7);
}


Hierbei wird der erste Befehl folgendermaßen umgesetzt:

10:         PORTC = (PORTC & 0xF7) | ((data & 0x01)<<3);
IN      R18,0x15         In from I/O location
ANDI    R18,0xF7         Logical AND with immediate
MOV     R20,R24          Copy register
CLR     R21              Exclusive OR
MOVW    R24,R20          Copy register pair
ANDI    R24,0x01         Logical AND with immediate
ANDI    R25,0x00         Logical AND with immediate
LDI     R22,0x03         Load immediate
LSL     R24              Add without carry
ROL     R25              Add with carry
DEC     R22              Decrement
BRNE    -0x04            Branch if status flag cleared
OR      R18,R24          Logical OR
OUT     0x15,R18         Out to I/O location

Warum werden denn hier die logischen Operationen mit 16 Bit ausgeführt
(R24/R25) wenn ich doch nur 8 Bit brauche? In diesem Code könnte man
ohne weiteres knapp 50% wegoptimieren.
Ich bin noch grün hinter den Ohren, was C betrifft - mache ich etwas
falsch oder ist das einfach so?
Prinzipiell könnte ich schon mit diesem Overhead leben - aber wenns
nicht unbedingt sein muß, verzichte ich auch gerne drauf ;)

Gruß
Thomas

von Rufus T. Firefly (Gast)


Lesenswert?

Daß die logischen Operationen mit 16 Bits ausgeführt werden, wird daran
liegen, daß die von Dir verwendeten Konstanten vom Compiler als
16-Bit-Konstanten (genaugenommen: int) behandelt werden.

Du musst durch ein geeignetes Suffix mitteilen, daß die Konstante einen
anderen Datentyp haben soll.

Allerdings hab' ich keines gefunden, das auf 8-Bit-unsigned verweist.

Unsigned generell wird durch Anhängen eines U signalisiert:

  wert = 1234U;

Long (32 Bit) wird durch Anhängen eines L signalisiert

  wert = 1234L;


Alternativ könntest Du mal ausprobieren, was geschieht, wenn Du mit
char-Konstanten arbeitest:

  void set_outputs(uint8_t data)
  {
    PORTC = (PORTC & '\xF7') | ((data & '\x01')<<3);
    PORTD = (PORTD & '\x03') | ((data & '\x7E')<<1);
    PORTB = (PORTB & '\xFE') | ((data & '\x80')>>7);
  }

von Jörg Wunsch (Gast)


Lesenswert?

Typecast auf uint8_t sollte besser sein.

In der Tat könnte der GCC hier besser optimieren (das darf er, solange
dabei dasselbe rauskommt wie bei der type promotion auf 16 bits), hier
zeigt sich, dass die meiste Arbeit im GCC eben auf 32- und 64-bit
Architekturen gemacht wird, wo man sowas gar nicht erst bemerkt.

von thkais (Gast)


Lesenswert?

Hallo,
danke für eure Tipps - jetzt weiß ich auch, was ein "typecast" ist
;)

Mit der folgenden Änderung der Zeile:

PORTC = (uint8_t)(PORTC & 0xF7) | (uint8_t)((data & 0x01)<<3);

wird nun dieser Code produziert - das gefällt mir wesentlich besser.

 in  r25, 0x15  ; 21
 andi  r25, 0xF7  ; 247
 andi  r24, 0x01  ; 1
 add  r24, r24
 add  r24, r24
 add  r24, r24
 or  r25, r24
 out  0x15, r25  ; 21

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.