std::string_view
сделал это в C ++ 17 и широко рекомендуется использовать его вместо const std::string&
.
Одна из причин - производительность.
Может кто-нибудь объяснить, как именно std::string_view
/ будет быстрее, чем const std::string&
при использовании в качестве типа параметра? (давайте предположим, что в вызываемом номере не сделано ни одной копии)
c++
string
c++17
string-view
Patryk
источник
источник
std::string_view
это просто абстракция пары (char * begin, char * end). Вы используете его при созданииstd::string
ненужной копии.std::string
(string_view может принимать необработанные массивы, векторы,std::basic_string<>
с распределителями, отличными от заданных по умолчанию и т. Д. И т. Д. И т. Д. Да, и другие строковые_виды, очевидно)Ответы:
std::string_view
быстрее в нескольких случаях.Во-первых,
std::string const&
требуется , чтобы данные находились вstd::string
массиве C, а не в необработанном массиве C,char const*
возвращались API C,std::vector<char>
производились каким-либо механизмом десериализации и т. Д. Преобразование избегаемого формата позволяет избежать копирования байтов и (если строка длиннее, чем SBO¹ для конкретнойstd::string
реализации) избегает выделения памяти.Распределения в этом
string_view
случае не выполняются , но было бы, если быfoo
взялиstd::string const&
вместоstring_view
.Вторая действительно важная причина заключается в том, что она позволяет работать с подстроками без копирования. Предположим, вы анализируете 2-гигабайтную строку json (!) ². Если вы анализируете его
std::string
, каждый такой узел синтаксического анализа, где хранятся имя или значение узла, копирует исходные данные из строки размером 2 ГБ в локальный узел.Вместо этого, если вы анализируете его в
std::string_view
s, узлы ссылаются на исходные данные. Это может сэкономить миллионы выделений и сократить вдвое требования к памяти при разборе.Ускорение, которое вы можете получить, просто смешно.
Это крайний случай, но другие случаи «получить подстроку и работать с ней» также могут привести к приличному ускорению
string_view
.Важной частью решения является то, что вы теряете при использовании
std::string_view
. Это не так много, но это что-то.Вы теряете неявное нулевое завершение, и это все. Таким образом, если одна и та же строка будет передана 3 функциям, каждая из которых требует нулевого терминатора,
std::string
может быть целесообразно преобразовать в один раз. Таким образом, если известно, что вашему коду нужен нулевой терминатор, и вы не ожидаете, что строки будут передаваться из буферов в стиле C или тому подобное, возможно, возьмите astd::string const&
. В противном случае возьмитеstd::string_view
.Если
std::string_view
бы был флаг, который указывал, что он был завершен нулем (или что-то более причудливое), он удалил бы даже эту последнюю причину использоватьstd::string const&
.Есть случай, когда взятие
std::string
без неconst&
является оптимальным по сравнению сstd::string_view
. Если вам нужно владеть копией строки на неопределенный срок после вызова, эффективный захват по значению. Вы будете либо в случае SBO (и без выделения, всего несколько копий символов для его дублирования), либо вы сможете переместить выделенный в куче буфер в локальныйstd::string
. Имея две перегрузкиstd::string&&
иstd::string_view
может быть быстрее, но лишь незначительно, и это вызвало бы скромное разрастание кода (который может стоить вам все преимущества скорости).¹ Оптимизация малого буфера
² Фактический вариант использования.
источник
Одним из способов повышения производительности string_view является то, что он позволяет легко удалять префиксы и суффиксы. Под капотом string_view можно просто добавить размер префикса к указателю на некоторый строковый буфер или вычесть размер суффикса из счетчика байтов, это обычно быстро. С другой стороны, std :: string должен копировать свои байты, когда вы делаете что-то вроде substr (таким образом вы получаете новую строку, которой принадлежит ее буфер, но во многих случаях вы просто хотите получить часть исходной строки без копирования). Пример:
С помощью std :: string_view:
Обновить:
Я написал очень простой тест, чтобы добавить реальные цифры. Я использовал потрясающую библиотеку бенчмарков Google . Контрольными функциями являются:
Полученные результаты
(x86_64 linux, gcc 6.2, "
-O3 -DNDEBUG
"):источник
Есть две основные причины:
string_view
является слайсом в существующем буфере, он не требует выделения памятиstring_view
передается по значению, а не по ссылкеПреимущества наличия среза множественны:
char const*
илиchar[]
без выделения нового буфераЛучшая и более стабильная производительность во всем.
Передача по значению также имеет преимущества перед передачей по ссылке, поскольку дает псевдонимы.
В частности, когда у вас есть
std::string const&
параметр, нет гарантии, что ссылочная строка не будет изменена. В результате компилятор должен повторно извлекать содержимое строки после каждого вызова в непрозрачный метод (указатель на данные, длину, ...).С другой стороны, при передаче
string_view
по значению компилятор может статически определить, что никакой другой код не может изменить длину и указатели данных, которые теперь находятся в стеке (или в регистрах). В результате он может «кэшировать» их через вызовы функций.источник
Единственное, что он может сделать, это избежать конструирования
std::string
объекта в случае неявного преобразования из строки с нулевым символом в конце:источник
const std::string str{"goodbye!"}; foo(str);
вероятно , не будет быстрее с string_view, чем с string &string_view
будет медленным, поскольку он должен копировать два указателя, а не один указатель вconst string&
?std::string_view
в основном просто обертка вокругconst char*
. А передачаconst char*
означает, что в системе будет меньше указателя по сравнению с передачейconst string*
(илиconst string&
), потому чтоstring*
подразумевает что-то вроде:Очевидно, что для передачи константных аргументов первый указатель является излишним.
ps Одно существенное различие между
std::string_view
иconst char*
, тем не менее, состоит в том, что представления string_view не обязательно должны заканчиваться нулем (они имеют встроенный размер), и это допускает случайное соединение на месте более длинных строк.источник
std::string_view
с просто фантазииconst char*
, точка. GCC реализует их так:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
std::string const*
. И эта диаграмма непонятна. @ n.caillou: Ваш собственный комментарий уже более точен, чем ответ. Это делаетstring_view
больше, чем просто фантазиюchar const*
- это действительно очевидно.std::string const*
иstd::string const&
так же, не так ли?