Ich habe viele kleine Daten, die ich in einen größeren Datentyp schieben möchte. Nehmen wir an, dass dies hypothetisch ein Datum und eine Uhrzeit ist. Die naheliegende Methode ist über ein Bitfeld wie dieses.
%Vor%Nun wollen wir so tun, als ob diese Sache so geordnet ist, dass die zuerst deklarierten Dinge eine höhere Bedeutung haben als die später deklarierten. Wenn ich also die Bits durch den ersten Buchstaben der Variablen repräsentiere, würde es so aussehen:
%Vor%Schließlich wollen wir so tun, als deklariere ich einfach ein unsigniertes long und teile es mit Masken und Verschiebungen auf, um die gleichen Dinge zu tun.
%Vor% Hier ist meine Frage:
Sind die folgenden Methoden für den Zugriff auf Minuten, Stunden usw. äquivalent in Bezug darauf, was der Computer tun muss? Oder gibt es eine trickreiche Methode, die der Compiler / Computer mit den Bitfeldern verwendet.
und
%Vor%usw.
Der Compiler erzeugt dieselben Anweisungen, die Sie explizit für den Zugriff auf die Bits schreiben würden. Also erwarte nicht, dass es mit Bitfeldern schneller ist.
Tatsächlich, genau genommen mit Bitfeldern, steuern Sie nicht, wie sie im Wort der Daten positioniert sind (es sei denn, Ihr Compiler gibt Ihnen zusätzliche Garantien. Ich meine, dass der C99-Standard keine definiert). Wenn Sie Masken von Hand ausführen, können Sie die beiden am häufigsten aufgerufenen Felder als erste und letzte in der Reihe platzieren, da in diesen beiden Positionen eine Operation statt zwei zur Isolierung des Feldes benötigt wird.
Diese werden wahrscheinlich den gleichen Maschinencode erstellen, aber wenn es wirklich wichtig ist, benchmarken Sie es. Oder, noch besser, benutze das Bitfeld, weil es einfacher ist!
Schnelles Testen von gcc ergibt:
%Vor%gegen
%Vor%Dies ist eine Little-Endian-Maschine, also sind die Zahlen anders, aber die drei Anweisungen sind fast gleich.
Es ist vollständig plattform- und kompilerabhängig. Einige Prozessoren, insbesondere Mikrocontroller, weisen Bitadressierungsanweisungen oder bitadressierbaren Speicher auf, und der Compiler kann diese direkt verwenden, wenn Sie integrierte Sprachkonstrukte verwenden. Wenn Sie Bit-Masking verwenden, um Bits auf einem solchen Prozessor zu bearbeiten, muss der Compiler intelligenter sein, um die potenzielle Optimierung zu erkennen.
Auf den meisten Desktop-Plattformen würde ich vorschlagen, dass Sie die kleinen Dinge schwitzen, aber wenn Sie es wissen müssen, sollten Sie es testen, indem Sie den Code profilieren oder timieren oder den generierten Code analysieren. Beachten Sie, dass Sie abhängig von den Compiler-Optimierungsoptionen und sogar von verschiedenen Compilern sehr unterschiedliche Ergebnisse erhalten können.
In diesem Beispiel würde ich das Bitfeld manuell verwenden.
Aber nicht wegen Zugriffen. Aber wegen der Fähigkeit, zwei dt's zu vergleichen.
Am Ende erzeugt der Compiler immer besseren Code als Sie (da der Compiler im Laufe der Zeit besser wird und niemals Fehler macht), aber dieser Code ist einfach genug, dass Sie wahrscheinlich optimalen Code schreiben werden (aber das ist die Art der Mikrooptimierung, die Sie sollten mach dir keine Sorgen).
Wenn Ihr dt eine Ganzzahl ist, die wie folgt formatiert ist:
%Vor%Dann können Sie sie natürlich so vergleichen.
%Vor%Bei Verwendung einer Bitfeldstruktur sieht der Code wie folgt aus:
%Vor%Natürlich könntest du das Beste aus beiden Welten erhalten, indem du eine Union verwendest, die die Struktur auf der einen Seite hat und die int als Alternative. Offensichtlich hängt dies genau davon ab, wie Ihr Compiler funktioniert, und Sie müssen testen, ob die Objekte in die richtigen Positionen gebracht werden (aber dies wäre der perfekte Ort, um etwas über TDD zu erfahren.)
Nur wenn Ihre Architektur explizit eine Reihe von Anweisungen für die bitweise Bearbeitung und den Zugriff enthält.
Hängt davon ab. Wenn Sie Bitfelder verwenden, dann lassen Sie den Compiler sich Sorgen darüber machen, wie die Daten gespeichert werden sollen (Bitfelder sind praktisch vollständig implementierungsdefiniert), was bedeutet:
Der Compiler organisiert normalerweise das Layout der Struktur, so dass die zweite Annahme zutrifft, auf Kosten der Gesamtgröße der Struktur.
Der Compiler fügt wahrscheinlich Padding zwischen jedem Mitglied ein, um den Zugriff auf jedes Feld zu erleichtern.
Andererseits, wenn Sie nur alles in einem einzigen unsigned long
(oder einem Array von Zeichen) speichern, liegt es an Ihnen, einen effizienten Zugriff zu implementieren, aber Sie haben eine Garantie für das Layout. Es wird eine feste Größe einnehmen, und es wird keine Polsterung geben. Und das bedeutet, dass das Kopieren des Werts möglicherweise weniger teuer wird. Und es wird portabler (vorausgesetzt, Sie verwenden einen int-Typ fester Größe statt nur unsigned int
).
Der Compiler kann manchmal den Zugriff auf die Bitfelder in einer nicht intuitiven Angelegenheit kombinieren. Ich habe den erzeugten Code (gcc 3.4.6 für sparc) einmal beim Zugriff auf 1-Bit-Einträge, die in einem conditionnal -Ausdrücken verwendet wurden, zerlegt. Der Compiler fusionierte den Zugriff auf die Bits und führte den Vergleich mit Ganzzahlen durch. Ich werde versuchen, die Idee zu reproduzieren (nicht bei der Arbeit und kann nicht auf den Quellcode zugreifen, der beteiligt war):
%Vor%wurde zu etwas ähnlichem kompiliert (ich weiß, dass der Beigeordnete in meinem Beispiel das Gegenteil ist, aber nur zur Vereinfachung des Beispiels).
%Vor%Es gibt noch einen anderen Punkt, den man in Betracht ziehen sollte, wenn man Bitfelder verwenden möchte (sie sind oft besser lesbar als Bit-Kungfu). Wenn man ein paar Bits übrig hat, kann es nützlich sein, ganze Bytes für bestimmte Felder zu reservieren durch den Prozessor. Ein 8-Bit-Feld, das ausgerichtet ist, kann mit einem Befehl zum Verschieben von Bytes geladen werden und benötigt keinen Maskierungsschritt.
Tags und Links c c++ bit-manipulation