Ich arbeite fast ausschließlich in C ++ 11/14 und erschaudere normalerweise, wenn ich Code wie diesen sehe:
std::int64_t mArray;
mArray |= someMask << 1;
Dies ist nur ein Beispiel. Ich spreche von bitweiser Manipulation im Allgemeinen. Gibt es in C ++ wirklich irgendeinen Grund? Das oben Genannte ist irritierend und fehleranfällig, während Sie mit a std::bitset
Folgendes tun können:
- Sie können die Größe der
std::bitset
Vorlage nach Bedarf einfacher ändern, indem Sie einen Vorlagenparameter anpassen und die Implementierung den Rest erledigen lassen - Verbringen Sie weniger Zeit damit, herauszufinden, was los ist (und möglicherweise Fehler zu machen), und schreiben Sie
std::bitset
in ähnlicher Weise wie instd::array
anderen Datencontainern.
Meine Frage ist; gibt es keinen Grund nicht zu verwenden std::bitset
, nicht zur Rückwärtskompatibilität über primitive Typen?
c++
c++11
bitwise-operators
quant
quelle
quelle
std::bitset
wird zur Kompilierungszeit festgelegt. Das ist der einzige Nachteil, den ich mir vorstellen kann.std::bitset
Vs-C-Stil (z. B.int
), die auch zur Kompilierungszeit behoben wird.std::bitset
nicht verfügbar war (oder dem Autor bekannt war), und es gab keinen Grund, den zu verwendenden Code umzuschreibenstd::bitset
.bitset
eine ist, aber ein kleiner Vektor oder eine Menge vonint
s (von Bitindex) könnte auch legitim sein. Die Philosophie von C / C ++ verbirgt diese Auswahlkomplexitäten nicht vor dem Programmierer.Antworten:
Aus logischer (nicht technischer) Sicht gibt es keinen Vorteil.
Jeder einfache C / C ++ - Code kann in ein geeignetes "Bibliothekskonstrukt" eingeschlossen werden. Nach einer solchen Umhüllung wird die Frage, ob dies vorteilhafter als das ist, zur strittigen Frage.
Unter dem Gesichtspunkt der Geschwindigkeit sollte C / C ++ es dem Bibliothekskonstrukt ermöglichen, Code zu generieren, der genauso effizient ist wie der einfache Code, den es umschließt. Dies unterliegt jedoch:
Mit dieser Art von nichttechnischem Argument können "fehlende Funktionen" von jedermann hinzugefügt werden und werden daher nicht als Nachteil gewertet.
Integrierte Anforderungen und Einschränkungen können jedoch nicht mit zusätzlichem Code überwunden werden. Im Folgenden wird argumentiert, dass die Größe von
std::bitset
eine Konstante für die Kompilierungszeit ist. Daher wird sie zwar nicht als Nachteil gewertet, sie wirkt sich jedoch immer noch auf die Auswahl des Benutzers aus.Unter ästhetischen Gesichtspunkten (Lesbarkeit, Wartungsfreundlichkeit usw.) gibt es einen Unterschied.
Es ist jedoch nicht ersichtlich, dass der
std::bitset
Code den einfachen C-Code sofort gewinnt. Man muss sich größere Teile des Codes (und nicht irgendein Spielzeugbeispiel) ansehen, um festzustellen, ob die Verwendung vonstd::bitset
die menschliche Qualität des Quellcodes verbessert hat.Die Geschwindigkeit der Bitmanipulation hängt vom Codierungsstil ab. Der Codierungsstil wirkt sich sowohl auf die C / C ++ - Bitmanipulation
std::bitset
als auch auf diese aus, wie im Folgenden erläutert wird.Wenn Sie Code
operator []
schreiben, mit dem Sie jeweils ein Bit lesen und schreiben, müssen Sie dies mehrmals tun, wenn mehr als ein Bit bearbeitet werden muss. Gleiches gilt für den C-Code.Allerdings
bitset
hat auch andere Betreiber, wieoperator &=
,operator <<=
etc., die auf der vollen Breite des bitset arbeitet. Da der zugrunde liegende Computer häufig mit 32-Bit-, 64-Bit- und manchmal mit 128-Bit-Code (mit SIMD) gleichzeitig (in der gleichen Anzahl von CPU-Zyklen) arbeiten kann, wurde Code entwickelt, um diese Multi-Bit-Vorgänge zu nutzen kann schneller sein als "schleifenförmiger" Bitmanipulationscode.Die allgemeine Idee heißt SWAR (SIMD innerhalb eines Registers) und ist ein Unterthema unter Bitmanipulationen .
Einige C ++ - Anbieter implementieren möglicherweise
bitset
zwischen 64-Bit und 128-Bit mit SIMD. Einige Anbieter tun dies möglicherweise nicht (aber möglicherweise auch). Wenn Sie wissen müssen, was die Bibliothek des C ++ - Herstellers tut, können Sie sich nur die Demontage ansehen.Ob
std::bitset
es Einschränkungen gibt, kann ich an zwei Beispielen erläutern.std::bitset
muss zum Zeitpunkt der Kompilierung bekannt sein. Um ein Array von Bits mit dynamisch gewählter Größe zu erstellen, muss man verwendenstd::vector<bool>
.std::bitset
bietet keine Möglichkeit zum Extrahieren eines aufeinanderfolgenden Schnitts von N Bits aus einem größerenbitset
von M Bits.Der erste Punkt ist grundlegend, was bedeutet, dass Menschen, die dynamisch große Bitsets benötigen, andere Optionen wählen müssen.
Die zweite kann überwunden werden, weil man eine Art Adapter schreiben kann, um die Aufgabe auszuführen, selbst wenn der Standard
bitset
nicht erweiterbar ist.Es gibt bestimmte Arten von erweiterten SWAR-Operationen, die nicht ab Werk bereitgestellt werden
std::bitset
. Über diese Operationen kann man auf dieser Website über Bit-Permutationen lesen . Diese kann man wie gewohnt selbstständig implementieren und aufbauenstd::bitset
.In Bezug auf die Diskussion über die Leistung.
Eine Warnung: Viele Leute fragen, warum (etwas) aus der Standardbibliothek viel langsamer ist als irgendein einfacher C-Code. Ich würde das vorausgesetzte Wissen über Mikrobenchmarking hier nicht wiederholen, aber ich habe nur den folgenden Rat: Stellen Sie sicher, dass Sie ein Benchmarking im "Release-Modus" durchführen (mit aktivierten Optimierungen) und dass der Code nicht beseitigt wird (Beseitigung toter Codes) oder nicht beseitigt wird aus einer Schleife gehisst (schleifeninvariante Codebewegung) .
Da wir im Allgemeinen nicht sagen können, ob jemand (im Internet) die Mikrobenchmarks richtig gemacht hat, können wir eine verlässliche Schlussfolgerung nur ziehen, indem wir unsere eigenen Mikrobenchmarks machen, die Details dokumentieren und der öffentlichen Überprüfung und Kritik unterziehen. Es tut nicht weh, Mikrobenchmarks zu wiederholen, die andere schon einmal gemacht haben.
quelle
std::bitset
. Es gibt keine Garantie für die Speicherkonsistenz (instd::bitset
), was bedeutet, dass es nicht zwischen Kernen geteilt werden soll. Leute, die es über Kerne hinweg teilen müssen, werden dazu neigen, ihre eigene Implementierung aufzubauen. Wenn Daten zwischen verschiedenen Kernen geteilt werden, ist es üblich, sie an der Cache-Zeilengrenze auszurichten. Andernfalls sinkt die Leistung und es treten weitere Fallstricke auf, die nicht der Atomizität entsprechen. Ich habe nicht genug Wissen, um einen Überblick darüber zu geben, wie eine parallelisierbare Implementierung von erstellt werden kannstd::bitset
.bitset
Willen.Dies trifft sicherlich nicht in allen Fällen zu, aber gelegentlich kann ein Algorithmus von der Effizienz des C-Bit-Twiddlings abhängen, um signifikante Leistungssteigerungen zu erzielen. Das erste Beispiel, das mir in den Sinn kommt, ist die Verwendung von Bitboards , clevere Ganzzahlcodierungen von Brettspielpositionen, um Schachmaschinen und dergleichen zu beschleunigen. Hier ist die feste Größe von Integer-Typen kein Problem, da Schachbretter sowieso immer 8 * 8 sind.
Betrachten Sie als einfaches Beispiel die folgende Funktion (entnommen aus dieser Antwort von Ben Jackson ), die eine Connect Four-Position auf Sieg testet:
quelle
std::bitset
wäre langsamer?