In einer C ++ - Frage zu Optimierung und Codestil bezogen sich mehrere Antworten auf "SSO" im Zusammenhang mit der Optimierung von Kopien von std::string
. Was bedeutet SSO in diesem Zusammenhang?
Ganz klar nicht "Single Sign On". "Shared String Optimization" vielleicht?
c++
string
optimization
Raedwald
quelle
quelle
std::string
implementiert" und eine andere fragt "was bedeutet SSO", müssen Sie absolut verrückt sein, um sie als dieselbe Frage zu betrachtenAntworten:
Hintergrund / Übersicht
Operationen mit automatischen Variablen ("vom Stapel", bei denen es sich um Variablen handelt, die Sie ohne Aufruf von
malloc
/ erstellennew
) sind im Allgemeinen viel schneller als Operationen mit dem freien Speicher ("der Heap", bei dem es sich um Variablen handelt, die mit erstellt werdennew
). 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ürdennew char [size]
. Dies verhindert einen Stapelüberlauf für sehr große Zeichenfolgen, kann jedoch langsamer sein, insbesondere bei Kopiervorgängen. Als Optimierungstd::string
erstellen viele Implementierungen ein kleines automatisches Array, so etwas wiechar [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 anzurufennew
, 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 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
odersize_type
einem Zeiger einen hinzufügen möchten, wenn der Benutzer aufruftend
. 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:
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. Wennm_size <= 16
, dann werde ich alle Daten eingebenm_sso
, damit ich die Kapazität bereits kenne und den Zeiger auf die Daten nicht benötige. Wennm_size > 16
ja, dann brauche ich nichtm_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):Ich würde annehmen, dass die meisten Implementierungen eher so aussehen.
quelle
std::string const &
, das Abrufen der Daten eine einzelne Speicher-Indirektion ist, da die Daten am Ort der Referenz gespeichert werden. Wenn es keine Optimierung für kleine Zeichenfolgen gäbe, wären für den Zugriff auf die Daten zwei Speicher-Indirektionen erforderlich (erstens, um den Verweis auf die Zeichenfolge zu laden und deren Inhalt zu lesen, und zweitens, um den Inhalt des Datenzeigers in der Zeichenfolge zu lesen).SSO ist die Abkürzung für "Small String Optimization", eine Technik, bei der kleine Zeichenfolgen in den Hauptteil der Zeichenfolgenklasse eingebettet werden, anstatt einen separat zugewiesenen Puffer zu verwenden.
quelle
Wie bereits in den anderen Antworten erläutert, bedeutet SSO Small / Short String Optimization . Die Motivation für diese Optimierung ist der unbestreitbare Beweis, dass Anwendungen im Allgemeinen viel kürzere Zeichenfolgen als längere Zeichenfolgen verarbeiten.
Wie von David Stone in seiner obigen Antwort erläutert , verwendet die
std::string
Klasse einen internen Puffer, um Inhalte bis zu einer bestimmten Länge zu speichern. Dadurch entfällt die Notwendigkeit, Speicher dynamisch zuzuweisen. Dies macht den Code effizienter und schneller .Diese andere verwandte Antwort zeigt deutlich, dass die Größe des internen Puffers von der
std::string
Implementierung abhängt , die von Plattform zu Plattform variiert (siehe Benchmark-Ergebnisse unten).Benchmarks
Hier ist ein kleines Programm, das den Kopiervorgang vieler Zeichenfolgen mit derselben Länge bewertet. Es beginnt mit dem Drucken der Zeit zum Kopieren von 10 Millionen Zeichenfolgen mit der Länge = 1. Dann wird es mit Zeichenfolgen mit der Länge = 2 wiederholt. Es wird fortgesetzt, bis die Länge 50 beträgt.
Wenn Sie dieses Programm ausführen möchten, sollten Sie dies
./a.out > /dev/null
so tun , dass die Zeit zum Drucken der Zeichenfolgen nicht gezählt wird. Die wichtigen Zahlen werden gedrucktstderr
, sodass sie in der Konsole angezeigt werden.Ich habe Diagramme mit der Ausgabe meiner MacBook- und Ubuntu-Computer erstellt. Beachten Sie, dass die Zeit zum Kopieren der Zeichenfolgen sehr groß ist, wenn die Länge einen bestimmten Punkt erreicht. Dies ist der Moment, in dem Zeichenfolgen nicht mehr in den internen Puffer passen und die Speicherzuordnung verwendet werden muss.
Beachten Sie auch, dass auf dem Linux-Computer der Sprung erfolgt, wenn die Länge der Zeichenfolge 16 erreicht. Auf dem MacBook erfolgt der Sprung, wenn die Länge 23 erreicht. Dies bestätigt, dass SSO von der Plattformimplementierung abhängt.
Ubuntu
Macbook Pro
quelle