Diese Antwort gibt einen schönen Überblick über die Short String Optimization (SSO). Ich möchte jedoch genauer wissen, wie es in der Praxis funktioniert, insbesondere in der libc ++ - Implementierung:
Wie kurz muss die Zeichenfolge sein, um sich für SSO zu qualifizieren? Hängt dies von der Zielarchitektur ab?
Wie unterscheidet die Implementierung beim Zugriff auf die Zeichenfolgendaten zwischen kurzen und langen Zeichenfolgen? Ist es so einfach wie
m_size <= 16
oder ist es ein Flag, das Teil einer anderen Mitgliedsvariablen ist? (Ich stelle mir vor, dassm_size
oder ein Teil davon auch zum Speichern von Zeichenfolgendaten verwendet werden könnte).
Ich habe diese Frage speziell für libc ++ gestellt, weil ich weiß, dass SSO verwendet wird. Dies wird sogar auf der libc ++ - Homepage erwähnt .
Hier sind einige Beobachtungen nach dem Betrachten der Quelle :
libc ++ kann mit zwei leicht unterschiedlichen Speicherlayouts für die Zeichenfolgenklasse kompiliert werden. Dies wird durch das _LIBCPP_ALTERNATE_STRING_LAYOUT
Flag gesteuert . Beide Layouts unterscheiden auch zwischen Little-Endian- und Big-Endian-Maschinen, sodass wir insgesamt 4 verschiedene Varianten haben. Ich werde im Folgenden das "normale" Layout und Little-Endian annehmen.
Unter der Annahme, dass dies size_type
4 Bytes und value_type
1 Byte sind, würden die ersten 4 Bytes eines Strings im Speicher folgendermaßen aussehen:
// short string: (s)ize and 3 bytes of char (d)ata
sssssss0;dddddddd;dddddddd;dddddddd
^- is_long = 0
// long string: (c)apacity
ccccccc1;cccccccc;cccccccc;cccccccc
^- is_long = 1
Da die Größe der kurzen Zeichenfolge in den oberen 7 Bits liegt, muss sie beim Zugriff verschoben werden:
size_type __get_short_size() const {
return __r_.first().__s.__size_ >> 1;
}
In ähnlicher Weise verwendet der Getter und Setter für die Kapazität eines langen Strings __long_mask
, um das zu umgehenis_long
Bit zu umgehen.
Ich suche immer noch nach einer Antwort auf meine erste Frage, dh welchen Wert würde __min_cap
die Kapazität von kurzen Strings für verschiedene Architekturen haben?
Andere Standardbibliotheksimplementierungen
Diese Antwort gibt einen schönen Überblick über std::string
Speicherlayouts in anderen Standardbibliotheksimplementierungen.
quelle
string
Header hier finden , ich überprüfe es im Moment :)Antworten:
Die libc ++
basic_string
ist so konzipiert, dass siesizeof
auf allen Architekturen 3 Wörter enthältsizeof(word) == sizeof(void*)
. Sie haben das Long / Short-Flag und das Größenfeld in der Kurzform korrekt zerlegt.In der Kurzform gibt es 3 Wörter, mit denen man arbeiten kann:
char
, 1 Byte geht an die nachfolgende Null (libc ++ speichert immer eine nachfolgende Null hinter den Daten).Dies lässt 3 Wörter minus 2 Bytes übrig, um eine kurze Zeichenfolge zu speichern (dh die größte
capacity()
ohne Zuordnung).Auf einem 32-Bit-Computer passen 10 Zeichen in die kurze Zeichenfolge. sizeof (string) ist 12.
Auf einem 64-Bit-Computer passen 22 Zeichen in die kurze Zeichenfolge. sizeof (string) ist 24.
Ein wichtiges Entwurfsziel war die Minimierung
sizeof(string)
, während der interne Puffer so groß wie möglich gemacht wurde. Das Grundprinzip besteht darin, die Bewegungskonstruktion und die Bewegungszuweisung zu beschleunigen. Je größer diesizeof
, desto mehr Wörter müssen Sie während einer Zugkonstruktion oder einer Zugzuweisung bewegen.Die lange Form benötigt mindestens 3 Wörter, um den Datenzeiger, die Größe und die Kapazität zu speichern. Deshalb habe ich die Kurzform auf die gleichen 3 Wörter beschränkt. Es wurde vorgeschlagen, dass eine Größe von 4 Wörtern eine bessere Leistung haben könnte. Ich habe diese Designauswahl nicht getestet.
_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT
Es gibt ein Konfigurationsflag namens,
_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT
das die Datenelemente so neu anordnet, dass sich das "lange Layout" ändert von:zu:
Die Motivation für diese Veränderung ist der Glaube, dass Putten
__data_
erste Mal aufgrund einer besseren Ausrichtung einige Leistungsvorteile hat. Es wurde versucht, die Leistungsvorteile zu messen, und es war schwierig zu messen. Dies wird die Leistung nicht verschlechtern und möglicherweise etwas verbessern.Die Flagge sollte mit Vorsicht verwendet werden. Es ist ein anderes ABI, und wenn es versehentlich mit einem libc ++ gemischt wird,
std::string
das mit einer anderen Einstellung von kompiliert wurde_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT
, entstehen Laufzeitfehler.Ich empfehle, dieses Flag nur von einem Anbieter von libc ++ zu ändern.
quelle
string
sind alle 0 Bits. Das macht die Standardkonstruktion sehr effizient. Und wenn Sie bereit sind, die Regeln zu biegen, manchmal sogar frei. Sie könnten beispielsweisecalloc
speichern und es einfach als voll von standardmäßig erstellten Zeichenfolgen deklarieren.int
s gespeichert werden, sodass die Klasse auf 64-Bit-Architekturen auf nur 16 Byte gepackt werden kann?sizeof
. Gleichzeitigchar
geht der interne Puffer für von 14 auf 22, was ein ziemlich guter Vorteil ist.Die libc ++ - Implementierung ist etwas kompliziert. Ich werde das alternative Design ignorieren und einen kleinen Endian-Computer annehmen:
Hinweis:
__compressed_pair
ist im Wesentlichen ein Paar, das für die Optimierung der leeren Basis optimiert wurde , auch bekannt alstemplate <T1, T2> struct __compressed_pair: T1, T2 {};
; In jeder Hinsicht können Sie es als reguläres Paar betrachten. Ihre Bedeutung kommt nur zum Ausdruck, weil siestd::allocator
staatenlos und damit leer ist.Okay, das ist ziemlich roh, also lasst uns die Mechanik überprüfen! Intern rufen viele Funktionen auf,
__get_pointer()
die selbst aufrufen ,__is_long
um festzustellen, ob die Zeichenfolge die Darstellung__long
oder verwendet__short
:Um ehrlich zu sein, bin ich mir nicht sicher, ob dies Standard C ++ ist (ich kenne die anfängliche Subsequenz-Bereitstellung in
union
, weiß aber nicht, wie sie mit einer anonymen Vereinigung und einem zusammengewürfelten Aliasing zusammenwirkt), aber eine Standardbibliothek darf die definierte Implementierung nutzen Verhalten sowieso.quelle
__min_cap
für verschiedene Architekturen bewertet werden würde. Ich bin nicht sicher, wassizeof()
zurückkehren wird und wie es durch Aliasing beeinflusst wird.3 * the size of one pointer
in diesem Fall 12 Oktette auf einem 32-Bit-Bogen und 24 auf einem 64-Bit-Bogen.