Ich muss eine große Datenmenge (für ein Arduino) in einem Programm verfolgen, während ich mich um eine ganze Reihe anderer Geschäfte kümmere.
Ich habe mit einer Struktur wie dieser begonnen:
struct MyStruct
{
// note: these names might as well be foo bar baz
uint8_t color;
boolean state;
uint8_t area;
uint8_t number;
uint8_t len;
};
Ich habe eine Reihe von 800 davon. Bei jeweils 5 Bytes sind das 4 KB oder die Hälfte des RAM auf dem Arduino Mega.
Ich habe zu einer Struktur wie dieser gewechselt:
struct MyStruct
{
uint8_t color : 3; // max value 7
// uint8_t state : 1; *** moved, thanks Ignacio ***
uint8_t area : 5; // m.v. 31
uint8_t number;
uint8_t len : 5; // m.v. 31
uint8_t state : 1; // m.v. 1
};
Ich verstehe, dass dies die Größe jeder Instanz auf 3 Bytes reduziert, was insgesamt 2,4 KB in meinem Array entspricht.
Ich habe gelesen, dass in einigen Implementierungen / Chipsätzen die Verwendung von Bitfeldern in Strukturen zu einer weniger effizienten Ausführung führen kann. Natürlich ist dies eine effizientere Nutzung des Speichers, aber meine Frage ist:
Wie geht der Arduino mit Bitfeldern um? Wird es besser, schlechter oder in Bezug auf Geschwindigkeit oder Geschwindigkeit vernachlässigbar anders sein, wenn es durch ein Array von Bitfeldstrukturen iteriert? Gibt es eine gute Möglichkeit, dies zu testen?
quelle
Antworten:
Was Sie sehen, ist der klassische Zeit-Speicher-Kompromiss. Die Bitfelder sind im Speicher kleiner, benötigen jedoch mehr Zeit für die Bearbeitung. Sie können zählen, dass die Bitfelder unabhängig vom Prozessor langsamer sind.
Sie verwenden das Wort effizient , aber dieses Wort hat keine bestimmte Bedeutung ohne eine Metrik für das, was gut oder schlecht ist. Wenn Sie nur 8 KB RAM haben und die Verwendung des Speichers schlecht ist, kann die Zeit günstig sein. Wenn Sie Echtzeitbeschränkungen haben, ist die Verwendung von Zeit schlecht und der Speicher kann billig sein. Im Allgemeinen können Sie sich nur aus diesem Kompromiss herauskaufen. Mit anderen Worten, wenn Sie Zeit und Gedächtnis schlecht finden, geben Sie Geld aus und verwenden Sie einen größeren Chip. Es gibt keine einheitliche Antwort auf das, was gut oder schlecht ist. Dies ist einer der Gründe, warum es für Mikrocontroller so viele Möglichkeiten gibt, dass Menschen den Chip an die Anwendung und die Anwendung an den Chip anpassen.
Das Auffüllen der Bits eines Bitfelds ist langsamer als das Füllen vollständiger Bytes. Nehmen wir zum Beispiel
Der erste einfache Fall wird nur:
Der zweite macht so etwas wie:
Wenn alle Ihre Vorgänge Daten in das Bitfeld packen oder aus dem Bitfeld entpacken, sollten Sie mit einer langsameren Ausführung rechnen. Bitfelder sind eine Art Komprimierung - sie kosten Ticks.
Das Bewegen in Bitfeldern ist jedoch schneller, da weniger Bytes geladen und gespeichert werden müssen. Wenn Sie dieses Array sortieren, kann die geringere Anzahl von Bytes von Vorteil sein. Wenn Sie sie über eine serielle Schnittstelle übertragen, kann die komprimierte Größe des Bitfelds ein Gewinner sein.
Also in Bezug auf Ihre Frage:
Der einzige gute Weg, dies zu testen, besteht darin, Testfälle für beide Ansätze mit einem Muster zu schreiben, das genau zu Ihrer Anwendung passt. Es ist wirklich wichtig, welche Mischung von Operationen Sie ausführen, um zu entscheiden, ob der Unterschied vernachlässigbar oder signifikant ist.
Verwenden Sie bei dieser Art von Optimierungsexperimenten auf jeden Fall die Quellcodeverwaltung für Ihr Projekt. Sie können mit nur wenigen Klicks ein lokales GIT- oder Mercurial-Repository erstellen. Wenn Sie eine Kette von Kontrollpunkten beibehalten, können Sie Ihren Code zerreißen und die Auswirkungen verschiedener Implementierungen untersuchen. Wenn Sie falsch abbiegen, können Sie im Repository einfach zum letzten guten Punkt zurückkehren und einen anderen Pfad ausprobieren.
(Randnotiz: Dieses Mal besteht der Speicherkompromiss auch in die entgegengesetzte Richtung. Wenn Sie die Compileroptionen für Desktop-Klassenprozessoren durchsehen, finden Sie etwas, das als Strukturpacken bezeichnet wird . Mit dieser Option können Sie leere Bytes zwischen Einzelbytefeldern hinzufügen, so dass diese Bleiben Sie an Wort- oder Doppelwortgrenzen ausgerichtet. Dies mag verrückt erscheinen, um RAM absichtlich wegzuwerfen, aber auf Prozessoren mit 16 oder 32 Bit breiten Registern und Bussen können sogenannte wort- oder dword-ausgerichtete Speicheroperationen schneller sein als byteweise Operationen.)
quelle
Eine Diskussion der Bitfelder wäre nicht vollständig, ohne Folgendes zu erwähnen:
[Kernighan & Ritchie, Programmiersprache C, 2. Auflage , Anhang A-8.3]
Andere Poster, einschließlich Sie selbst, haben sich mit dem Kompromiss zwischen Raum und Zeit befasst. Was jedoch manchmal übersehen wird (möglicherweise nicht relevant für Ihre Anwendung -?), Ist die vollständige Implementierungsabhängigkeit des Speicherlayouts . Dies ist wichtig, wenn Anwendungen diese Daten mit einem anderen Prozess teilen: Lesen oder Schreiben von Hardware, deren Registerbitzuweisungen notwendigerweise festgelegt sind; Übermittlung der Daten an ein anderes System (über Netzwerk oder Speichergerät spielt keine Rolle); oder zu einer anderen Anwendung auf demselben System, die möglicherweise mit einem anderen Compiler oder einer anderen Version des Compilers kompiliert wurde.
quelle
Ein kleiner Tipp - ich sehe, dass die Struktur keine 1B für 8-Bit-CPU ist. Am Ende Ihrer Struktur können Sie hinzufügen Polsterung 2 - Bit Länge oder umdenken Erhöhung der Größe des Staates .
quelle