Эффективная конкатенация строк в C ++

Я слышал, как несколько человек выражали беспокойство по поводу оператора «+» в std :: string и различных обходных решениях для ускорения конкатенации. Действительно ли это необходимо? Если да, то каков наилучший способ конкатенации строк в C ++?

Дополнительная работа, вероятно, не стоит того, если вам действительно не нужна эффективность. Вероятно, вы будете иметь гораздо лучшую эффективность, просто используя operator + =.

Теперь после этого отказа от ответственности я отвечу на ваш реальный вопрос …

Эффективность строкового classа STL зависит от реализации STL, который вы используете.

Вы можете гарантировать эффективность и контролировать себя, выполняя конкатенацию вручную с помощью встроенных функций.

Почему оператор + не эффективен:

Взгляните на этот интерфейс:

 template  basic_string operator+(const basic_string& s1, const basic_string& s2) 

Вы можете видеть, что после каждого + возвращается новый объект. Это означает, что каждый раз используется новый буфер. Если вы делаете тонну дополнительных операций, это неэффективно.

Почему вы можете сделать его более эффективным:

  • Вы гарантируете эффективность, а не доверяете делегату, чтобы сделать это эффективно для вас
  • class std :: string ничего не знает о максимальном размере вашей строки и о том, как часто вы будете конкатенировать с ней. У вас могут быть эти знания и вы можете делать что-то на основе этой информации. Это приведет к меньшему перераспределению.
  • Вы будете управлять буферами вручную, чтобы быть уверенным, что вы не будете копировать всю строку в новые буферы, когда вы этого не хотите.
  • Вы можете использовать стек для ваших буферов вместо кучи, который намного эффективнее.
  • string + operator создаст новый строковый объект и вернет его, используя новый буфер.

Соображения для реализации:

  • Следите за длиной строки.
  • Держите указатель до конца строки и начала, или просто запустите, и используйте start + length как смещение, чтобы найти конец строки.
  • Убедитесь, что буфер, в котором вы храните свою строку, достаточно велик, поэтому вам не нужно перераспределять данные
  • Используйте strcpy вместо strcat, поэтому вам не нужно перебирать длину строки, чтобы найти конец строки.

Структура канатной информации:

Если вам нужны действительно быстрые конкатенации, подумайте об использовании структуры данных каната .

Зарезервируйте свое конечное пространство до этого, затем используйте метод append с буфером. Например, предположим, что ваша конечная длина строки должна составлять 1 миллион символов:

 std::string s; s.reserve(1000000); while (whatever) { s.append(buf,len); } 

Я бы не стал беспокоиться об этом. Если вы сделаете это в цикле, строки всегда будут выделять память для минимизации перераспределения – просто используйте operator+= в этом случае. И если вы сделаете это вручную, что-то вроде этого или дольше

 a + " : " + c 

Затем он создает временные файлы – даже если компилятор может устранить некоторые копии возвращаемого значения. Это связано с тем, что в последовательно вызываемом operator+ он не знает, ссылается ли ссылочный параметр на именованный объект или на временный, возвращаемый из operator+ вызов. Я бы предпочел не беспокоиться об этом, прежде чем не профилировать в первую очередь. Но давайте возьмем пример, чтобы показать это. Сначала мы вводим круглые скобки, чтобы сделать привязку понятной. Я помещаю аргументы непосредственно после объявления функции, которое используется для ясности. Ниже я покажу, каким получилось следующее выражение:

 ((a + " : ") + c) calls string operator+(string const&, char const*)(a, " : ") => (tmp1 + c) 

Теперь в этом добавлении tmp1 – это то, что было возвращено первым вызовом operator + с показанными аргументами. Мы предполагаем, что компилятор действительно умный и оптимизирует копию возвращаемого значения. Таким образом, мы получаем одну новую строку, содержащую конкатенацию a и " : " . Теперь это происходит:

 (tmp1 + c) calls string operator+(string const&, string const&)(tmp1, c) => tmp2 ==  

Сравните это со следующим:

 std::string f = "hello"; (f + c) calls string operator+(string const&, string const&)(f, c) => tmp1 ==  

Он использует ту же функцию для временной и для именованной строки! Поэтому компилятор должен скопировать аргумент в новую строку и добавить к нему и вернуть его из тела operator+ . Он не может взять память о временном и добавить к этому. Чем больше выражение, тем больше копий строк должно быть выполнено.

Следующая Visual Studio и GCC будут поддерживать семантику перемещения c ++ 1x (дополняющую семантику копирования ) и ссылки rvalue в качестве экспериментального дополнения. Это позволяет выяснить, ссылается ли параметр на временный или нет. Это сделает такие дополнения удивительно быстрыми, так как все вышеперечисленное закончится одним «дополнительным конвейером» без копий.

Если это окажется узким местом, вы все равно можете

  std::string(a).append(" : ").append(c) ... 

Приложение append добавляет аргумент к *this а затем возвращает ссылку на себя. Таким образом, копирование временных объектов не производится. Или, альтернативно, можно использовать operator+= , но для исправления приоритета вам потребуются уродливые скобки.

Для большинства приложений это не имеет значения. Просто напишите свой код, блаженно не осознавая, как работает оператор +, и только берете дело в свои руки, если оно становится очевидным узким местом.

В отличие от .NET System.Strings, std :: string C ++ являются изменяемыми и поэтому могут быть созданы с помощью простой конкатенации так же быстро, как и с помощью других методов.

возможно, std :: stringstream?

Но я согласен с тем, что вы, вероятно, должны просто сохранить его под контролем и понятным, а затем профиль, чтобы увидеть, действительно ли у вас проблемы.

В Imperfect C ++ Мэтью Уилсон представляет динамический конкатенатор строк, который предварительно вычисляет длину конечной строки, чтобы иметь только одно выделение перед конкатенацией всех частей. Мы также можем реализовать статический конкатенатор, играя с шаблонами выражений .

Такая идея была реализована в STLport std :: string implementation – это не соответствует стандарту из-за этого точного взлома.

std::string operator+ выделяет новую строку и копирует две строки операндов каждый раз. повторяйте много раз, и он становится дорогим, O (n).

std::string append и operator+= с другой стороны, увеличивайте емкость на 50% каждый раз, когда строка должна расти. Это значительно уменьшает количество распределений памяти и операций копирования O (log n).

Для маленьких струн это не имеет значения. Если у вас есть большие строки, вам лучше их хранить, поскольку они находятся в векторе или в какой-то другой коллекции в качестве частей. И добавьте свой алгоритм для работы с таким набором данных, а не с одной большой строкой.

Я предпочитаю std :: ostringstream для сложной конкатенации.

Как и в большинстве случаев, легче делать что-то, чем делать это.

Если вы хотите выводить большие строки в графический интерфейс, может быть, что все, что вы выводите, может обрабатывать строки по частям лучше, чем как большая строка (например, конкатенация текста в текстовом редакторе – обычно они содержат строки как отдельные структуры).

Если вы хотите вывести в файл, streamи данных, а не создание большой строки и вывод этого.

Мне никогда не приходилось требовать ускорения конкатенации, если я удалил ненужную конкатенацию из медленного кода.

Самый простой массив символов, инкапсулированный в class, который отслеживает размер массива и количество выделенных байтов, является самым быстрым.

Хитрость заключается в том, чтобы сделать только одно большое распределение при запуске.

в

https://github.com/pedro-vicente/table-string

Ориентиры

Для Visual Studio 2015, assembly отладки x86, существенное улучшение над C ++ std :: string.

 | API | Seconds | ----------------------|----| | SDS | 19 | | std::string | 11 | | std::string (reserve) | 9 | | table_str_t | 1 | 
  • Эффективный способ удаления строки из текстового файла
  • Игнорирование параметра NULL в T-SQL
  • Как получить размер объекта в памяти?
  • Лучший способ изменить строку
  • Поведение коллекции мусора для String.intern ()
  • Как мне зарегистрироваться на C # без использования сторонних библиотек?
  • Выполнение стресс-теста в веб-приложении?
  • MySQL загружает данные infile - ускорение?
  • Неизвестные события в nodejs / v8 flashgraph с использованием perf_events
  • Разница в скорости при использовании встроенных строк против конкатенации в php5?
  • Каков ваш любимый инструмент профилирования (для C ++)
  • Interesting Posts

    В SQL, нормально ли для двух таблиц ссылаться друг на друга?

    Какие новые возможности добавляют пользовательские литералы к C ++?

    Как преобразовать значения big-endian и little-endian в C ++?

    Проблема с правами доступа к папке в Windows 7

    Сделать Excel открытым некоторые гиперссылки с браузером, отличным от по умолчанию

    Номер страницы больше не отображается (Word 2003)

    C ++ Прочитайте файл по строкам, затем разделите каждую строку, используя разделитель

    Восстановление Windows 7 MBR с загрузочного компакт-диска Hiren

    Как изменить значок док-станции Java-программы?

    jQuery document.ready vs самоназванная анонимная функция

    Как изменить пользовательский агент Firefox через about: config?

    Как iPhone может подключиться к другому устройству, отличному от iPhone, по беспроводной или Bluetooth?

    Преобразование прозрачного PNG в JPG с нечерным цветом фона

    В чем разница между абстрактным деревом синтаксиса и деревом синтаксиса бетона?

    Как установить конкретные ячейки, чтобы не вычислять в Excel?

    Давайте будем гением компьютера.