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.
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
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... :/
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;
}
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.
@ 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.
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.
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 nichtAVR verwendet ...
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.