Forum: Compiler & IDEs Bits aus Array extrahieren


von Ben (Gast)


Lesenswert?

Hallo zusammen,

ich benötige für ein Projekt sehr viele boolsche Flags (>100).
Da ich nicht jeweils ein ganzes Byte verschwenden will organisier ich 
das ganze in einem Byte-Array.
Der Zugriff erfolgt mittels Makros, um die dahinter liegende 
Datenstruktur zu verbergen.
1
uint8_t data[10];
2
#define getFlag(number) (((data[number / 8]) & (1 << (number % 8))) >> number)
3
4
...
5
void foo()
6
{
7
    flag = getFlag(15);
8
}

Lesen und Schreiben klappt auch, aber ich finde mein "Lese-Makro" 
hässlich. Und außerdem sehr unperformant auf einem ATmega.
For allem die variablen Shifts dürften hässlich werden...
Geht das irgendwie schöner oder performanter?

Was mir spontan einfällt wäre den Ausdruck "(1 << (number % 8))" im 
Makro durch einen LookUpTable-Zugriff zu ersetzen (uint8_8 lut[] = 
{0x01, 0x02, ...})
Sollte zumindest einen Shift ersparen, kostet dafür 8 Byte.

Wie würdet ihr das lösen?

Viele Grüße,
Ben

von Ben (Gast)


Lesenswert?

Ups, muss mich berichtigen, das Lese-Makro klappt so natürlich nicht. 
Der letzte Shift klappt natürlich nur beim ersten Byte. Das muss auch 
noch korrigiert werden.
Dadurch dürfte der Zugriff aber nur noch unperformanter werden... :/

von Peter II (Gast)


Lesenswert?

Ben schrieb:
> Das muss auch
> noch korrigiert werden.

das kannst du dem Compiler überlasse

[c]
uint8_t data[10];
#define getFlag(number) (((data[number / 8]) & (1 << (number % 8))))

...
void foo()
{
    flag = getFlag(15) ? 1 : 0;
}

von Rolf Magnus (Gast)


Lesenswert?

Ben schrieb:
> Lesen und Schreiben klappt auch, aber ich finde mein "Lese-Makro"
> hässlich.

Sie wären schon mal deutlich schöner, wenn du statt Nummern wie 15 
sprechende Namen verwendest.

> Und außerdem sehr unperformant auf einem ATmega.

Warum?

> For allem die variablen Shifts dürften hässlich werden...

Wieso variabel? Entscheidet sich erst zur Laufzeit, auf welches Flag du 
zugreifen willst?

> Geht das irgendwie schöner oder performanter?

Ich denke nicht, daß du es performanter hinbekommst. Schöner ist 
Ansichtssache. Manche finden es mit Bitfeldern eleganter.

von Falk B. (falk)


Lesenswert?

@ Ben (Gast)

>Lesen und Schreiben klappt auch, aber ich finde mein "Lese-Makro"
>hässlich. Und außerdem sehr unperformant auf einem ATmega.

Wie oft musst du die denn lesen? 1 Million Mal / s?

>For allem die variablen Shifts dürften hässlich werden...

Mein Gott, soll jede noch so komplexe Operation in 1 Takt ausgeführt 
werden?

>Was mir spontan einfällt wäre den Ausdruck "(1 << (number % 8))" im
>Makro durch einen LookUpTable-Zugriff zu ersetzen (uint8_8 lut[] =
>{0x01, 0x02, ...})
>Sollte zumindest einen Shift ersparen, kostet dafür 8 Byte.

8 Shifts dauern 8 Takte. Ein Arrayzugriff ist nicht viel schneller. Ggf 
mit einem Switch und INLINE Funktion, das könnte einen TICK schneller 
sein. Ausserdem würde ich schauen, ob der Compiler schlau genug ist, die 
/8 Division durch drei Rechtsschiebungen zu ersetzen. Also 
Inlinefunktion kann man die Variablengröße minimieren (uint8), als Macro 
nicht (INT, 16 Bit)

>Wie würdet ihr das lösen?

So wie du, das passt schon.

von Falk B. (falk)


Lesenswert?

1
#include <avr/io.h>
2
3
uint8_t flags[10];
4
5
inline uint8_t get_flag(uint8_t number) {
6
    uint8_t i = number/8;
7
    switch (number & 0x7) {
8
        case 0: return flags[i] & 0x01;
9
        case 1: return flags[i] & 0x02;
10
        case 2: return flags[i] & 0x04;
11
        case 3: return flags[i] & 0x08;
12
        case 4: return flags[i] & 0x10;
13
        case 5: return flags[i] & 0x20;
14
        case 6: return flags[i] & 0x40;
15
        case 7: return flags[i] & 0x80;
16
    }
17
    return 0;
18
}
19
20
int main(void) {
21
22
    volatile uint8_t i;
23
24
    i=get_flag(8);
25
    
26
}

von Juergen R. (stumpjumper)


Lesenswert?

Liese sich das nicht mit bitfeldern lösen?
Ohne es jetzt ausprobiert zu haben könnte doch mit einem Zeiger auf das 
Array dann wieder jedes byte als Array ansprechen.
Ich denke da z.B an eine SPS-ähnliche Adressierung "Byte.bit"

Wie gesagt, ich hab es nicht getestet. Ich möchte lediglich einen 
Denkanstoß beitragen.

von Juergen R. (stumpjumper)


Lesenswert?


von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Zum Codebeispiel von Falk sei angemerkt, daß das so nur dann nötig ist, 
wenn a) wirklich ein AVR eingesetzt wird und b) es auf Geschwindigkeit 
ankommt.

Es gibt Architekturen, die nicht diese vom AVR bekannten Probleme mit 
Schiebeoperationen haben, die verfügen über einen sogenannten "barrel 
shifter", so daß sie ohne Laufzeitunterschiede um beliebig viele Bits 
hin- und herschieben können, was bedeutet, daß dort Code so geschrieben 
werden kann, wie er gemeint ist, und nicht Schiebeoperationen krampfhaft 
vermieden werden müssen.

Da der Threadstarter nicht erwähnt hat, welche Architektur er einsetzt, 
könnte es ja sein, daß er zufälligerweise nicht AVR verwendet ...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

"Und außerdem sehr unperformant auf einem ATmega."

von Rolf Magnus (Gast)


Lesenswert?

Falk Brunner schrieb:
> "Und außerdem sehr unperformant auf einem ATmega."

Da steht nur, wo es langsam ist, nicht wo es eingesetzt wird ;-)
SCNR

Aber im Ernst: Ich möchte nochmal wiederholen, daß die Problematik mit 
der Schieberei sich sowieso nur dann ergibt, wenn die Flagnummer nicht 
zur Compilezeit bekannt ist. Sonst wird das vom Compiler eh alles 
wegoptimiert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ah, da. Gut "versteckt", nicht offensichtlich.

: Bearbeitet durch User
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.