- std::string e; Пустые строки, ничего необычного. - std::string_view e; - ssa e; - stringa e; - lstringa<20> e; - lstringa<40> e; - std::string e = "Test text"; Короткий литерал помещается во внутренний буфер std::string, время тратится только на копирование 10 байтов. - std::string_view e = "Test text"; И string_view, и ssa - по сути одно и то же: указатель на текст и его длина. - ssa e = "Test text"; - stringa e = "Test text"; stringa при инициализации константным литералом так же сохраняет только указатель на текст и его длину. - lstringa<20> e = "Test text"; Внутреннего буфера хватает для размещения символов, время уходит только на копирование байтов. - lstringa<40> e = "Test text"; - std::string e = "123456789012345678901234567890"; Вот тут уже литерал не помещается во внутренний буфер, возникает аллокация и копирование 30-и байтов. Но как же отстает аллокация под Windows от Linux'а, 20 vs 70 ns... - std::string_view e = "123456789012345678901234567890"; string_view и ssa по прежнему ничего не делают, кроме запоминания указателя на текст и его размера. - ssa e = "123456789012345678901234567890"; - stringa e = "123456789012345678901234567890"; stringa на константных литералах не отстает! - lstringa<20> e = "123456789012345678901234567890"; lstringa<20> может вместить в себя до 23 символов, Очевидно, что для 30-и символов уже нужна аллокация. - lstringa<40> e = "123456789012345678901234567890"; А в lstringa<40> влезает до 47 символов, так что просто копируется 30 байтов. - std::string e = "Test text"; auto c{e}; Строка в пределах SSO, так что просто копирует байты. - std::string_view e = "Test text"; auto c{e}; - ssa e = "Test text"; auto c{e}; ssa и string_view не владеют строкой, копируется только информация о строке. - stringa e = "Test text"; auto c{e}; Копирование stringa происходит быстро, особенно если она инициализирована литералом. - lstringa<20> e = "Test text"; auto c{e}; В обоих случаях хватает внутреннего буфера. - lstringa<40> e = "Test text"; auto c{e}; Только копируются байты. - std::string e = "123456789012345678901234567890"; auto c{e}; Копирования длинной строки вызывает аллокацию, SSO уже не хватает. И снова как же отстаёт аллокация под Windows... - std::string_view e = "123456789012345678901234567890"; auto c{e}; - ssa e = "123456789012345678901234567890"; auto c{e}; - stringa e = "123456789012345678901234567890"; auto c{e}; А вот у stringa копирование литерала не зависит от его длины, сравни с предыдущим бенчмарком. - lstringa<20> e = "123456789012345678901234567890"; auto c{e}; Не влезает, аллокация. - lstringa<40> e = "123456789012345678901234567890"; auto c{e}; Уложили во внутренний буфер. - std::string::find; Здесь "победила дружба", у всех типов по колонке примерно одинаково. Однако, Windows и Linux явно в разных весовых категориях. - std::string_view::find; - ssa::find; - stringa::find; - lstringa<20>::find; - lstringa<40>::find; - std::string copy{str_with_len_N};/15 - std::string copy{str_with_len_N};/16 Явно виден скачок, где заканчивается SSO и начинается аллокация. Обратите внимание, что WASM - 32-битный, и там размер SSO у std::string меньше, насколько я помню, 11 символов + 0. - std::string copy{str_with_len_N};/23 Дальше просто добавляется время на копирование байтов. - std::string copy{str_with_len_N};/24 - std::string copy{str_with_len_N};/32 - std::string copy{str_with_len_N};/64 - std::string copy{str_with_len_N};/128 - std::string copy{str_with_len_N};/256 - std::string copy{str_with_len_N};/512 - std::string copy{str_with_len_N};/1024 - std::string copy{str_with_len_N};/2048 - std::string copy{str_with_len_N};/4096 Чем длиннее строка, тем дольше создаётся копия. - stringa copy{str_with_len_N};/15 Здесь stringa инициализируется не литералом, а значит, должна сама хранить символы. - stringa copy{str_with_len_N};/16 Под WASM SSO у stringa составляет 15 символов. Кроме того, собиралось без поддержки потоков, поэтому возможно атомарный инкремент заменён на обычный, судя по времени. - stringa copy{str_with_len_N};/23 SSO в stringa до 23 символов, и даже 23 копируются быстрее, чем 15 в std::string. - stringa copy{str_with_len_N};/24 Всё, не влезаем в SSO, а значит, используем shared буфер. Добавляется время на атомарный инкремент счётчика. - stringa copy{str_with_len_N};/32 - stringa copy{str_with_len_N};/64 - stringa copy{str_with_len_N};/128 - stringa copy{str_with_len_N};/256 - stringa copy{str_with_len_N};/512 - stringa copy{str_with_len_N};/1024 - stringa copy{str_with_len_N};/2048 - stringa copy{str_with_len_N};/4096 И как видно, кроме инкремента нет накладных расходов, время копирования не зависит от длины строки. - lstringa<16> copy{str_with_len_N};/15 lstringa<16> использует SSO до 23 символов. А в WASM 32-битная архитектура, SSO до 19 символов. - lstringa<16> copy{str_with_len_N};/16 - lstringa<16> copy{str_with_len_N};/23 - lstringa<16> copy{str_with_len_N};/24 И после начинает вести себя при копировании, как std::string. - lstringa<16> copy{str_with_len_N};/32 - lstringa<16> copy{str_with_len_N};/64 - lstringa<16> copy{str_with_len_N};/128 - lstringa<16> copy{str_with_len_N};/256 - lstringa<16> copy{str_with_len_N};/512 - lstringa<16> copy{str_with_len_N};/1024 - lstringa<16> copy{str_with_len_N};/2048 - lstringa<16> copy{str_with_len_N};/4096 - lstringa<512> copy{str_with_len_N};/8 А вот lstringa<512> имеет гораздо больший внутренний буфер и копирует символы без аллокации. - lstringa<512> copy{str_with_len_N};/16 - lstringa<512> copy{str_with_len_N};/32 - lstringa<512> copy{str_with_len_N};/64 - lstringa<512> copy{str_with_len_N};/128 - lstringa<512> copy{str_with_len_N};/256 - lstringa<512> copy{str_with_len_N};/512 Даже 512 символов копируются быстрее, чем одна аллокация или атомарный инкремент. - lstringa<512> copy{str_with_len_N};/1024 А дальше уже как у всех - lstringa<512> copy{str_with_len_N};/2048 - lstringa<512> copy{str_with_len_N};/4096 - std::string s = "123456789"; int res = std::strtol(s.c_str(), 0, 10); В simstr для конвертации в число достаточно куска строки, нет нужды в null терминированности. Ближайший аналог такого поведения "std::from_chars", но он к сожалению очень ограничен по возможностям. Здесь я попытался произвести тесты, близкие по логике к работе std::from_chars - std::string_view s = "123456789"; std::from_chars(s.data(), s.data() + s.size(), res, 10); from_chars требует точного указания основания счисления, не допускает знаков плюс, пробелов, префиксов 0x и т.п. - stringa s = "123456789"; int res = s.to_int Здесь для to_int заданы такие же ограничения - проверять переполнение, десятичная система, без лидирующих пробелов и знака плюс - ssa s = "123456789"; int res = s.to_int - lstringa<20> s = "123456789"; int res = s.to_int - std::string s = "abcDef"; int res = std::strtol(s.c_str(), 0, 16); Всё то же, только для 16ричной системы - std::string_view s = "abcDef"; std::from_chars(s.data(), s.data() + s.size(), res, 16); - stringa s = "abcDef"; int res = s.to_int - ssa s = "abcDef"; int res = s.to_int - lstringa<20> s = "abcDef"; int res = s.to_int - std::string s = " 123456789"; int res = std::strtol(s.c_str(), 0, 0); А здесь уже парсинг произвольного числа. - stringa s = " 123456789"; int res = s.to_int; // Check overflow - ssa s = " 123456789"; int res = s.to_int; // No check overflow - std::stringstream str; ... str << "abbaabbaabbaabba"; - std::string str; ... str += "abbaabbaabbaabba"; - lstringa<8> str; ... str += "abbaabbaabbaabba"; - lstringa<128> str; ... str += "abbaabbaabbaabba"; Чем больше внутренний буфер, тем меньше раз требуется аллокация, тем быстрее результат. - lstringa<512> str; ... str += "abbaabbaabbaabba"; - lstringa<1024> str; ... str += "abbaabbaabbaabba"; - std::stringstream str; ... str << str_var << "abbaabbaabbaabba"; - std::string str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<8> str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<128> str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<512> str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<1024> str; ... str += str_var + "abbaabbaabbaabba"; - std::stringstream str; ... str << str_var << "abbaabbaabbaabba"; - std::string str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<8> str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<128> str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<512> str; ... str += str_var + "abbaabbaabbaabba"; - lstringa<1024> str; ... str += str_var + "abbaabbaabbaabba"; - std::stringstream str; ... str << str_var1 << str_var2; - std::string str; ... str += str_var1 + str_var2; - lstringa<16> str; ... str += str_var1 + str_var2; - lstringa<128> str; ... str += str_var1 + str_var2; - lstringa<512> str; ... str += str_var1 + str_var2; - lstringa<1024> str; ... str += str_var1 + str_var2; - std::stringstream str; str << "test = " << k << " times"; - std::string str = "test = " + std::to_string(k) + " times"; - char buf[100]; sprintf(buf, "test = %u times", k); std::string str = buf; - std::string str = std::format("test = {} times", k); - lstringa<8> str; str.format("test = {} times", k); В simstr format с первого раза не помещается в такую строку без аллокации. - lstringa<32> str; str.format("test = {} times", k); А в такую помещается. Используйте сразу буфера подходящего размера. - lstringa<8> str = "test = " + k + " times"; Результат не помещается в SSO, возникает аллокация. - lstringa<32> str = "test = " + k + " times"; А здесь и ниже - результат укладывается в SSO. Ещё раз - используйте сразу буфера подходящего размера. - stringa str = "test = " + k + " times"; Под WASM размер SSO 15 символов, что явно не хватает для размещения результата, отсюда и такое время. - std::string::find + substr + std::strtol - ssa::splitter + ssa::as_int - Naive (and wrong) replace symbols with std::string find + replace Это наивная реализация, которая неверно отработает на таких заменах, как 'a'->'b' и 'b'->'a'. Но если замены не конфликтуют, то работает быстро. - replace symbols with std::string find_first_of + replace Дальше уже правильные реализации, не зависящие от конфликтующих замен. - replace symbols with std::string_view find_first_of + copy - replace runtime symbols with string expressions and without remembering all search results - replace runtime symbols with simstr and memorization of all search results - replace const symbols with string expressions and without remembering all search results - replace const symbols with string expressions and memorization of all search results - Short Naive (and wrong) replace symbols with std::string find + replace - Short replace symbols with std::string find_first_of + replace - Short replace symbols with std::string_view find_first_of + copy - Short replace runtime symbols with string expressions and without remembering all search results - Short replace runtime symbols with simstr and memorization of all search results - Short replace const symbols with string expressions and without remembering all search results - Short replace const symbols with string expressions and memorization of all search results - replace bb to ---- in 64 std::string Тут проверяется тяжелый случай - замена подстроки на более длинную. Обычная реализация несколько раз передвигает хвост. - replace bb to ---- in 64 lstringa<8> - replace bb to ---- in 64 str by init stringa - replace bb to ---- in 256 std::string - replace bb to ---- in 256 lstringa<8> - replace bb to ---- in 256 str by init stringa - replace bb to ---- in 512 std::string - replace bb to ---- in 512 lstringa<8> - replace bb to ---- in 512 str by init stringa - replace bb to ---- in 1024 std::string - replace bb to ---- in 1024 lstringa<8> - replace bb to ---- in 1024 str by init stringa - replace bb to ---- in 2048 std::string Чем длиннее строка, тем больше замедляется std::string - replace bb to ---- in 2048 lstringa<8> - replace bb to ---- in 2048 str by init stringa - replace bb to -- in 64 std::string Идеальный случай замены - на подстроку такой же длины - replace bb to -- in 64 lstringa<8> - replace bb to -- in 64 by init stringa - replace bb to -- in 256 std::string - replace bb to -- in 256 lstringa<8> - replace bb to -- in 256 by init stringa - replace bb to -- in 512 std::string - replace bb to -- in 512 lstringa<8> - replace bb to -- in 512 by init stringa - replace bb to -- in 1024 std::string - replace bb to -- in 1024 lstringa<8> - replace bb to -- in 1024 by init stringa - replace bb to -- in 2048 std::string - replace bb to -- in 2048 lstringa<8> - replace bb to -- in 2048 by init stringa - hashStrMapA emplace & find stringa; Вставляем в hashStrMapA 10000 stringa длиной от 30 до 50 символов, а потом ищем их в ней - std::unordered_map emplace & find std::string; То же самое c std::string и std::unordered_map - hashStrMapA emplace & find ssa; Теперь вставляем stringa, а ищем ssa - std::unordered_map emplace & find std::string_view; Вставляем std::string, а ищем std::string_view - Build func full name std::string; Обыденная задача, подобные часто могут встретится в работе: По неким данным сгенерировать текст. В этом случае по данным о неких функциях сформировать их полное имя с типами параметров и возвращаемого значения. Алгоритм на std::string. - Build func full name std::string 1; Почти тот же алгоритм, но несколько последовательных += к строке заменены на одно += + + +. - Build func full name std::stream; Строим имя функции через std::ostringstream и << - Build func full name stringa; Реализация на simstr строках и строковых выражениях. Инфа о параметрах добавляется в текущую строку - Build func full name stringa 1; Реализация на simstr строках и строковых выражениях. Инфа о параметрах добавляется во временную строку, а потом разом добавляется в текущую строку. Позволяет операции в цикле записать в одну строку, но чуть проигрывает по времени выполнения. - Пусто