Hintergrund / Übersicht
Operationen mit automatischen Variablen ("vom Stapel", bei denen es sich um Variablen handelt, die Sie ohne Aufruf von malloc
/ erstellen new
) sind im Allgemeinen viel schneller als Operationen mit dem freien Speicher ("der Heap", bei dem es sich um Variablen handelt, die mit erstellt werden new
). Die Größe der automatischen Arrays ist jedoch zur Kompilierungszeit festgelegt, die Größe der Arrays aus dem freien Speicher jedoch nicht. Darüber hinaus ist die Stapelgröße begrenzt (normalerweise einige MiB), während der freie Speicher nur durch den Arbeitsspeicher Ihres Systems begrenzt ist.
SSO ist die Short / Small String Optimization. A std::string
speichert die Zeichenfolge normalerweise als Zeiger auf den freien Speicher ("den Heap"), der ähnliche Leistungsmerkmale bietet, als ob Sie aufrufen würden new char [size]
. Dies verhindert einen Stapelüberlauf für sehr große Zeichenfolgen, kann jedoch langsamer sein, insbesondere bei Kopiervorgängen. Als Optimierung std::string
erstellen viele Implementierungen ein kleines automatisches Array, so etwas wie char [20]
. Wenn Sie eine Zeichenfolge mit 20 Zeichen oder weniger haben (in diesem Beispiel variiert die tatsächliche Größe), wird sie direkt in diesem Array gespeichert. Dies vermeidet die Notwendigkeit, überhaupt anzurufen new
, was die Dinge etwas beschleunigt.
BEARBEITEN:
Ich hatte nicht erwartet, dass diese Antwort so beliebt sein würde, aber da dies der Fall ist, möchte ich eine realistischere Implementierung geben, mit dem Vorbehalt, dass ich noch nie eine Implementierung von SSO "in the wild" gelesen habe.
Implementierungsdetails
A muss mindestens std::string
die folgenden Informationen speichern:
- Die Größe
- Die Kapazität
- Der Speicherort der Daten
Die Größe kann als std::string::size_type
oder als Zeiger auf das Ende gespeichert werden . Der einzige Unterschied besteht darin, ob Sie zwei Zeiger subtrahieren müssen, wenn der Benutzer aufruft, size
oder size_type
einem Zeiger einen hinzufügen möchten, wenn der Benutzer aufruft end
. Die Kapazität kann auch so oder so gespeichert werden.
Sie zahlen nicht für das, was Sie nicht verwenden.
Betrachten Sie zunächst die naive Implementierung basierend auf dem, was ich oben skizziert habe:
class string {
public:
// all 83 member functions
private:
std::unique_ptr<char[]> m_data;
size_type m_size;
size_type m_capacity;
std::array<char, 16> m_sso;
};
Für ein 64-Bit-System bedeutet dies im Allgemeinen, dass std::string
24 Bytes 'Overhead' pro Zeichenfolge plus weitere 16 für den SSO-Puffer vorhanden sind (16 werden hier aufgrund von Auffüllanforderungen anstelle von 20 ausgewählt). Es wäre nicht wirklich sinnvoll, diese drei Datenelemente plus eine lokale Zeichenfolge zu speichern, wie in meinem vereinfachten Beispiel. Wenn m_size <= 16
, dann werde ich alle Daten eingeben m_sso
, damit ich die Kapazität bereits kenne und den Zeiger auf die Daten nicht benötige. Wenn m_size > 16
ja, dann brauche ich nicht m_sso
. Es gibt absolut keine Überlappung, wo ich sie alle brauche. Eine intelligentere Lösung, die keinen Platz verschwendet, würde etwas ähnlicher aussehen (ungetestet, nur zu Beispielzwecken):
class string {
public:
// all 83 member functions
private:
size_type m_size;
union {
class {
// This is probably better designed as an array-like class
std::unique_ptr<char[]> m_data;
size_type m_capacity;
} m_large;
std::array<char, sizeof(m_large)> m_small;
};
};
Ich würde annehmen, dass die meisten Implementierungen eher so aussehen.
std::string
implementiert" und eine andere fragt "was bedeutet SSO", müssen Sie absolut verrückt sein, um sie als dieselbe Frage zu betrachten