Long effizient schreiben

OP #2503539
Lesenswert?

Hallo,

ich versuche gerade meinen Code zu optimieren und frage mich, welches 
die effizienteste Lösung ist eine Long-Variable aus 4 Bytes zusammen zu 
setzen. Bisher habe ich es so gemacht:
1
long Long;
2
unsigned char Byte1,Byte2,Byte3,Byte4;
3

4
Long   = Byte1 ; Long <<= 8; 
5
Long  += Byte2 ; Long <<= 8;
6
Long  += Byte3 ; Long <<= 8;
7
Long  += Byte4 ; Long <<= 8;

--> also mit Bitverschiebung. Die Frage ist ob es nicht bequemer und vor 
allem schneller geht, sprich mit weniger Taktzyklen.

Vielen Dank...
Gast #2503570
Lesenswert?

Eine Union ist das Mittel der Wahl. Die einzelnen Komponente liegen 
übereinandern.


union
{
 unsigned char vByte[ 4 ] ;     //  4 mal unsigned char (4 Bytes)
 long vLong ;                   //  1 mal long          (4 Bytes)
} value ;

value.cByte[0] = 100 ;

etc.
Moderator Persönliche Seite #2503572
Lesenswert?

Thomas M. schrieb:
> Long   = Byte1 ; Long <<= 8;
> Long  += Byte2 ; Long <<= 8;
> Long  += Byte3 ; Long <<= 8;
> Long  += Byte4 ; Long <<= 8;

Der letzte Shift ist hyperfluid und sogar falsch.

Intuitiv dachte ich, dass das Ersetzen von '+' durch '|' effizienter 
sei. Komischerweise generiert der AVR-GCC damit längeren Code...

Das hier geht wesentlich schneller:

    unsigned char * l = (unsigned char *) &Long;
    l[0] = Byte1;
    l[1] = Byte2;
    l[2] = Byte3;
    l[3] = Byte4;

Vorsicht:
Die Reihenfolge des Füllens ist abhängig vom Endian des Prozessors.

Gruß,

Frank
#2504807
Lesenswert?

nein.
Das ist sie nur, wenn es um Portabilität geht.
Auf einem Controller ist das eher selten sein.

Den portableren Weg über Shiften kennt er ja, und er will es schneller 
haben.
Dann bleibt noch der gezeigte Weg über die Zeiger und der mit union.

Was gefällt dir daran nicht?
Gast #2504869
Lesenswert?

Schlechte Wahl. Die pointer-Lösung basiert auf dem selben Prinzip wie 
die Lösung mit der Union, ist damit genauso wenig portabel, braucht aber 
wesentlich mehr Zyklen.

Wirklich portabel ist nur deine anfangs gezeigte Variante.

Oliver
Moderator Persönliche Seite #2504904
Lesenswert?

Oliver schrieb:
> Schlechte Wahl. Die pointer-Lösung basiert auf dem selben Prinzip wie
> die Lösung mit der Union, ist damit genauso wenig portabel, braucht aber
> wesentlich mehr Zyklen.

Ja, es ist dasselbe Prinzip wie die Lösung mit der Union, ist aber von 
der Schreibarbeit weniger aufwendig. Natürlich hast Du recht: Sie ist 
genauso wenig portabel wie die Union, da beide abhängig vom "Bytesex" 
(Endian) des µCs sind.

Aber im letzten Punkt gebe ich Dir nicht recht. Es kann nicht 
"wesentlich mehr" Zyklen brauchen als die Union-Lösung.

Hier ein Auszug aus der lss-Datei (AVR-gcc):

    unsigned char * l = (unsigned char *) &Long;
    l[0] = Byte1;
  b6:  80 91 61 00   lds  r24, 0x0061
  ba:  80 93 62 00   sts  0x0062, r24
    l[1] = Byte2;
  be:  80 91 60 00   lds  r24, 0x0060
  c2:  80 93 63 00   sts  0x0063, r24
    l[2] = Byte3;
  c6:  80 91 66 00   lds  r24, 0x0066
  ca:  80 93 64 00   sts  0x0064, r24
    l[3] = Byte4;
  ce:  80 91 67 00   lds  r24, 0x0067
  d2:  80 93 65 00   sts  0x0065, r24

Das ist verdammt kurz. Ich wüsste nicht, wo die Union-Lösung da noch ein 
Byte einsparen könnte. Von "wesentlich mehr als die Union-Lösung" kann 
daher keine Rede sein.

> Wirklich portabel ist nur deine anfangs gezeigte Variante.

Ja, leider. Die Shift-Version kostet das 5-7 fache an Code. Ich würde 
trotzdem immer die portable Variante vorziehen, außer man hätte die 
Möglichkeit, den Endian des verwendeten Prozessors per Preprocessor 
abzufragen. Dann könnte man portabel beide Varianten im Source 
platzieren, nämlich in etwa so:

    unsigned char * l = (unsigned char *) &Long;

#if BIG_ENDIAN == 1
    l[0] = Byte1;
    l[1] = Byte2;
    l[2] = Byte3;
    l[3] = Byte4;
#else
    l[0] = Byte4;
    l[1] = Byte3;
    l[2] = Byte2;
    l[3] = Byte1;
#endif

Gruß,

Frank
#2504911
Lesenswert?

Frank M. schrieb:
> außer man hätte die
> Möglichkeit, den Endian des verwendeten Prozessors per Preprocessor

Geht bei gcc über vordefinierte Makros.
Damit ist man nicht mehr vom System abhängig, sondern nur noch vom gcc, 
was evtl. eine sinnvollere Einschränkung ist.

Ansonsten kann man es auch mit C-Ausdrücken erreichen, die nur konstante 
Ausdrücke verwenden. Damit hat man gute Aussichten, daß der Compiler 
alles wegoptimieren kann und es zur Laufzeit nichts kostet und trotztdem 
portabel ist.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren