std::string_view
hat es auf C ++ 17 geschafft und es wird allgemein empfohlen, es anstelle von zu verwenden const std::string&
.
Einer der Gründe ist die Leistung.
Kann jemand erklären, wie genau std::string_view
schneller ist / sein wird als const std::string&
bei Verwendung als Parametertyp? (Nehmen wir an, es werden keine Kopien im Angerufenen angefertigt.)
c++
string
c++17
string-view
Patryk
quelle
quelle
std::string_view
ist nur eine Abstraktion des Paares (char * begin, char * end). Sie verwenden es, wennstd::string
eine unnötige Kopie erstellt wird.std::string
(string_view kann Raw-Arrays, Vektoren,std::basic_string<>
mit nicht standardmäßigen Allokatoren usw. usw. usw. akzeptieren . Oh, und andere string_views natürlich)Antworten:
std::string_view
ist in einigen Fällen schneller.Erstens
std::string const&
müssen sich die Daten in einemstd::string
und nicht in einem rohen C-Array befinden, daschar const*
von einer C-API zurückgegeben wird,std::vector<char>
von einer Deserialisierungs-Engine erzeugt wird usw. Die vermiedene Formatkonvertierung vermeidet das Kopieren von Bytes und (wenn die Zeichenfolge länger als die ist SBO¹ für die jeweiligestd::string
Implementierung) vermeidet eine Speicherzuordnung.In dem
string_view
Fall werden keine Zuweisungen vorgenommen , aber es würde geben, wennfoo
astd::string const&
anstelle von a genommen würdestring_view
.Der zweite wirklich große Grund ist, dass es das Arbeiten mit Teilzeichenfolgen ohne Kopie ermöglicht. Angenommen, Sie analysieren eine 2-Gigabyte-JSON-Zeichenfolge (!) ². Wenn Sie es analysieren
std::string
, kopiert jeder dieser Analyseknoten, auf dem der Name oder Wert eines Knotens gespeichert ist, die Originaldaten aus der 2-GB-Zeichenfolge auf einen lokalen Knoten.Wenn Sie es stattdessen auf
std::string_view
s analysieren , beziehen sich die Knoten auf die Originaldaten. Dies kann Millionen von Zuordnungen einsparen und den Speicherbedarf während des Parsens halbieren.Die Beschleunigung, die Sie bekommen können, ist einfach lächerlich.
Dies ist ein extremer Fall, aber auch andere Fälle, in denen Sie einen Teilstring erhalten und damit arbeiten, können zu angemessenen Beschleunigungen führen
string_view
.Ein wichtiger Teil der Entscheidung ist, was Sie durch die Verwendung verlieren
std::string_view
. Es ist nicht viel, aber es ist etwas.Sie verlieren die implizite Nullterminierung, und das war's auch schon. Wenn also dieselbe Zeichenfolge an drei Funktionen übergeben wird, für die alle ein Nullterminator erforderlich ist, kann eine Konvertierung in eine
std::string
einmal sinnvoll sein. Wenn bekannt ist, dass Ihr Code einen Null-Terminator benötigt und Sie keine Zeichenfolgen erwarten, die aus Puffern im C-Stil oder ähnlichem gespeist werden, nehmen Sie möglicherweise astd::string const&
. Ansonsten nimm einstd::string_view
.Wenn
std::string_view
ein Flag angegeben wäre, ob es nullterminiert ist (oder etwas ausgefalleneres), würde es sogar den letzten Grund für die Verwendung von a entfernenstd::string const&
.Es gibt einen Fall, in dem die Einnahme von a
std::string
mit noconst&
über a optimal iststd::string_view
. Wenn Sie nach dem Aufruf auf unbestimmte Zeit eine Kopie der Zeichenfolge besitzen müssen, ist die Verwendung von By-Value effizient. Sie werden entweder in der SBO Fall sein (und keine Zuweisungen, nur wenige Zeichen kopiert sie zu kopieren), oder Sie werden in der Lage sein zu bewegen den Heap-zugewiesenen Puffer in eine lokalestd::string
. Mit zwei Überlastungenstd::string&&
undstd::string_view
könnte schneller sein, aber nur geringfügig, und es wäre bescheiden Code aufblähen verursachen (die Sie alle der Geschwindigkeit Gewinne kosten könnte).¹ Optimierung kleiner Puffer
² Tatsächlicher Anwendungsfall.
quelle
Eine Möglichkeit, mit der string_view die Leistung verbessert, besteht darin, dass Präfixe und Suffixe einfach entfernt werden können. Unter der Haube kann string_view einfach die Präfixgröße zu einem Zeiger auf einen Zeichenfolgenpuffer hinzufügen oder die Suffixgröße vom Bytezähler subtrahieren. Dies ist normalerweise schnell. std :: string hingegen muss seine Bytes kopieren, wenn Sie so etwas wie substr ausführen (auf diese Weise erhalten Sie einen neuen String, dem der Puffer gehört, aber in vielen Fällen möchten Sie nur einen Teil des ursprünglichen Strings abrufen, ohne ihn zu kopieren). Beispiel:
Mit std :: string_view:
Aktualisieren:
Ich habe einen sehr einfachen Benchmark geschrieben, um einige reelle Zahlen hinzuzufügen. Ich habe eine großartige Google Benchmark-Bibliothek verwendet . Benchmarked-Funktionen sind:
Ergebnisse
(x86_64 Linux, gcc 6.2, "
-O3 -DNDEBUG
"):quelle
Es gibt zwei Hauptgründe:
string_view
ist ein Slice in einem vorhandenen Puffer, erfordert keine Speicherzuordnungstring_view
wird als Wert übergeben, nicht als ReferenzDie Vorteile einer Scheibe sind vielfältig:
char const*
oderchar[]
ohne Zuweisung eines neuen Puffers verwendenÜberall bessere und gleichmäßigere Leistung.
Das Übergeben von Werten hat auch Vorteile gegenüber dem Übergeben von Referenzen, da Aliasing.
Insbesondere, wenn Sie eine haben
std::string const&
Parameter haben, gibt es keine Garantie dafür, dass die Referenzzeichenfolge nicht geändert wird. Infolgedessen muss der Compiler den Inhalt der Zeichenfolge nach jedem Aufruf in eine undurchsichtige Methode (Zeiger auf Daten, Länge, ...) erneut abrufen.Andererseits
string_view
kann der Compiler beim Übergeben eines By-Werts statisch bestimmen, dass kein anderer Code die Länge und die Datenzeiger ändern kann, die sich jetzt auf dem Stapel (oder in Registern) befinden. Infolgedessen kann es sie über Funktionsaufrufe hinweg "zwischenspeichern".quelle
Eine Möglichkeit besteht darin, die Erstellung eines
std::string
Objekts bei einer impliziten Konvertierung aus einer nullterminierten Zeichenfolge zu vermeiden :quelle
const std::string str{"goodbye!"}; foo(str);
es mit string_view wahrscheinlich nicht schneller geht als mit string &string_view
nicht langsam sein, da zwei Zeiger im Gegensatz zu einem Zeiger kopiert werden müssenconst string&
?std::string_view
ist im Grunde nur ein Wrapper um einconst char*
. Und Übergebenconst char*
bedeutet, dass es im Vergleich zum Übergebenconst string*
(oderconst string&
) einen Zeiger weniger im System gibt , weil diesstring*
Folgendes impliziert:Für die Übergabe von const-Argumenten ist der erste Zeiger eindeutig überflüssig.
ps Ein wesentlicher Unterschied zwischen
std::string_view
undconst char*
wesentlicher besteht dennoch darin, dass die string_views nicht nullterminiert werden müssen (sie haben eine integrierte Größe), und dies ermöglicht das zufällige direkte Spleißen längerer Strings.quelle
std::string_view
s sind nur ausgefalleneconst char*
s, Punkt. GCC implementiert sie wieclass basic_string_view {const _CharT* _M_str; size_t _M_len;}
std::string const*
. Und dieses Diagramm ist unverständlich. @ n.caillou: Dein eigener Kommentar ist schon genauer als die Antwort. Das machtstring_view
mehr als "schickchar const*
" - es ist wirklich ganz offensichtlich.std::string const*
undstd::string const&
bist es auch, oder?