std::string_view
dotarł do C ++ 17 i zaleca się używanie go zamiast const std::string&
.
Jednym z powodów jest wydajność.
Czy ktoś może wyjaśnić, jak dokładnie std::string_view
jest / będzie szybszy, niż const std::string&
gdy zostanie użyty jako typ parametru? (załóżmy, że nie wykonano żadnych kopii w odbiorcy)
c++
string
c++17
string-view
Patryk
źródło
źródło
std::string_view
jest tylko abstrakcją pary (char * begin, char * end). Używasz go, gdy robieniestd::string
byłby niepotrzebną kopią.std::string
(string_view może akceptować surowe tablice, wektory,std::basic_string<>
z domyślnymi alokatorami itp. Itd. Itd. Och, i oczywiście inne string_views)Odpowiedzi:
std::string_view
jest szybszy w kilku przypadkach.Po pierwsze,
std::string const&
wymaga , aby dane znajdowały się wstd::string
, a nie w surowej tablicy C,char const*
zwróconej przez C API,std::vector<char>
wytworzonej przez jakiś silnik deserializacji itp. Unikana konwersja formatu pozwala uniknąć kopiowania bajtów i (jeśli łańcuch jest dłuższy niż SBO¹ dla konkretnejstd::string
implementacji) unika alokacji pamięci.W tej
string_view
sprawie nie dokonuje się żadnych alokacji , ale byłoby, gdybyfoo
wziąłstd::string const&
zamiast zamiaststring_view
.Drugim naprawdę ważnym powodem jest to, że pozwala na pracę z podciągami bez kopii. Załóżmy, że analizujesz 2-gigabajtowy ciąg Json (!) ². Jeśli go parsujesz
std::string
, każdy taki parsowany węzeł, w którym przechowują nazwę lub wartość węzła, kopiuje oryginalne dane z ciągu 2 gb do węzła lokalnego.Zamiast tego, jeśli parsujesz to do
std::string_view
s, węzły odnoszą się do oryginalnych danych. Pozwala to zaoszczędzić miliony przydziałów i zmniejszyć o połowę wymagania dotyczące pamięci podczas analizowania.Przyspieszenie, które można uzyskać, jest po prostu śmieszne.
Jest to skrajny przypadek, ale inne przypadki „zdobycia podciągu i pracy z nim” również mogą generować przyzwoite przyspieszenia
string_view
.Ważną częścią decyzji jest to, co tracisz, używając
std::string_view
. To niewiele, ale to jest coś.Tracisz domniemane zerowe zakończenie i to wszystko. Więc jeśli ten sam ciąg zostanie przekazany do 3 funkcji, z których każda wymaga zerowego terminatora, konwersja na
std::string
jeden raz może być mądra. Zatem jeśli wiadomo, że twój kod potrzebuje terminatora zerowego i nie oczekujesz ciągów zasilanych z buforów typu C lub podobnych, może weźstd::string const&
. W przeciwnym razie weźstd::string_view
.Gdyby
std::string_view
flaga oznaczała, że jeśli jest zerowa (lub coś bardziej wymyślnego), usunęłaby nawet ten ostatni powód, aby użyćstd::string const&
.Zdarzają się przypadki, w których wzięcie „
std::string
nie”const&
jest optymalne w stosunku dostd::string_view
. Jeśli po wywołaniu musisz posiadać kopię łańcucha na czas nieokreślony, efektywne jest przyjmowanie wartości według. Będziesz albo w przypadku SBO (bez przydziałów, wystarczy kilka kopii znaków, aby go zduplikować), albo będziesz mógł przenieść bufor przydzielony na stos do lokalnegostd::string
. Posiadanie dwóch przeciążeństd::string&&
istd::string_view
może być szybciej, ale tylko nieznacznie, a to spowodowałoby kodu skromną uwędzić (który może kosztować wszystkie zyski prędkość).¹ Optymalizacja małego bufora
² Rzeczywisty przypadek użycia.
źródło
Jednym ze sposobów poprawienia wydajności string_view jest to, że umożliwia łatwe usuwanie przedrostków i przyrostków. Pod maską string_view może po prostu dodać rozmiar prefiksu do wskaźnika do jakiegoś bufora łańcucha lub odjąć rozmiar sufiksu od licznika bajtów, zwykle jest to szybkie. z drugiej strony std :: string musi kopiować swoje bajty, gdy robisz coś takiego jak substr (w ten sposób otrzymujesz nowy ciąg, który jest właścicielem jego bufora, ale w wielu przypadkach po prostu chcesz uzyskać część oryginalnego ciągu bez kopiowania). Przykład:
Ze std :: string_view:
Aktualizacja:
Napisałem bardzo prosty test porównawczy, aby dodać prawdziwe liczby. Użyłem niesamowitej biblioteki testów Google . Funkcje testowane to:
Wyniki
(x86_64 linux, gcc 6.2, „
-O3 -DNDEBUG
”):źródło
Są 2 główne powody:
string_view
jest plasterkiem w istniejącym buforze, nie wymaga przydziału pamięcistring_view
jest przekazywany przez wartość, a nie przez odniesienieZalety posiadania wycinka są liczne:
char const*
lubchar[]
bez niegoLepsza i bardziej spójna wydajność na całym świecie.
Przekazywanie przez wartość ma również zalety w porównaniu z przekazywaniem przez odniesienie, ponieważ aliasing.
W szczególności, gdy masz
std::string const&
parametr, nie ma gwarancji, że łańcuch odniesienia nie zostanie zmodyfikowany. W rezultacie kompilator musi ponownie pobrać treść ciągu po każdym wywołaniu do nieprzejrzystej metody (wskaźnik do danych, długość, ...).Z drugiej strony, podczas przekazywania
string_view
wartości przez, kompilator może statycznie ustalić, że żaden inny kod nie może modyfikować długości i wskaźników danych na stosie (lub w rejestrach). W rezultacie może „buforować” je między wywołaniami funkcji.źródło
Jedną rzeczą, jaką może zrobić, jest uniknięcie budowy
std::string
obiektu w przypadku niejawnej konwersji z łańcucha zakończonego znakiem zerowym:źródło
const std::string str{"goodbye!"}; foo(str);
prawdopodobnie nie będzie szybciej z string_view niż z string &string_view
będzie powolny, ponieważ musi skopiować dwa wskaźniki zamiast jednego wskaźnikaconst string&
?std::string_view
jest w zasadzie tylko opakowaniemconst char*
. A przekazywanieconst char*
oznacza, że w systemie będzie o jeden wskaźnik mniej niż przekazywanieconst string*
(lubconst string&
), ponieważstring*
implikuje coś takiego:Oczywiście w celu przekazywania argumentów const pierwszy wskaźnik jest zbędny.
ps Jeden substancial różnica między
std::string_view
iconst char*
, mimo wszystko, jest to, że string_views nie muszą być zakończony zerem (mają wbudowany w rozmiarze), a to pozwala na losowe składanie w miejscu dłuższych łańcuchach.źródło
std::string_view
są po prostu fantazyjneconst char*
, kropka. GCC wdraża je w następujący sposób:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
std::string const*
. Ten schemat jest niezrozumiały. @ n.caillou: Twój własny komentarz jest już dokładniejszy niż odpowiedź. Tostring_view
coś więcej niż „fantazyjnechar const*
” - to naprawdę dość oczywiste.std::string const*
istd::string const&
jesteś taki sam, prawda?