Как StringBuilder работает внутри C #? - PullRequest
42 голосов
/ 29 июня 2011

Как работает StringBuilder?

Что он делает внутренне ?Использует ли он небезопасный код?И почему это так быстро (по сравнению с оператором +)?

Ответы [ 4 ]

66 голосов
/ 29 июня 2011

Когда вы используете оператор + для построения строки:

string s = "01";
s += "02";
s += "03";
s += "04";

, то при первом объединении мы создаем новую строку длиной четыре и копируем в нее «01» и «02» -четыре символа скопированы.На втором объединении мы создаем новую строку длиной шесть и копируем в нее «0102» и «03» - копируются шесть символов.На третьем этапе мы создаем строку длиной восемь и копируем в нее «010203» и «04» - восемь символов копируются.До сих пор для этой строки из восьми символов было скопировано всего 4 + 6 + 8 = 18 символов.Продолжайте.

...
s += "99";

На 98-м конкате мы создаем строку длиной 198 и копируем в нее «010203 ... 98» и «99».Это дает нам всего 4 + 6 + 8 + ... + 198 = много, чтобы сделать эту строку из 198 символов.

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

Что происходит, когда предположение неверно и массив заполняется?Есть две стратегии.В предыдущей версии фреймворка строитель строк перераспределял и копировал массив, когда он заполнялся, и удваивал его размер.В новой реализации построитель строк поддерживает связанный список относительно небольших массивов и добавляет новый массив в конец списка, когда старый заполняется.

Кроме того, как вы и предполагали, компоновщик строк может делать трюки с «небезопасным» кодом, чтобы повысить его производительность.Например, код, который записывает новые данные в массив, уже может проверить, что запись в массив будет в пределах границ.Отключая систему безопасности, можно избежать проверки на запись, которую в противном случае мог бы внести джиттер, чтобы убедиться, что каждая запись в массив безопасна.Конструктор строк выполняет ряд таких приемов, чтобы обеспечить повторное использование, а не перераспределение буферов, предотвращение ненужных проверок безопасности и т. Д.Я рекомендую против такого рода махинаций, если вы действительно не умеете правильно писать небезопасный код и действительно нуждаетесь в том, чтобы использовать все возможности.

15 голосов
/ 29 июня 2011
Я полагаю, что реализация

StringBuilder изменилась между версиями.Хотя в основном он поддерживает изменяемую структуру некоторой формы.Я полагаю, что использовал , чтобы использовать строку, которая все еще мутировала (используя внутренние методы) и просто удостоверился бы, что она никогда не будет мутирована после ее возвращения.

Причина StringBuilderбыстрее, чем использование конкатенации строк в цикле именно из-за изменчивости - она ​​не требует создания новой строки после каждой мутации, что будет означать копирование всех данных в строке и т. д.

Для одной конкатенации на самом деле немного эффективнее использовать +, чем StringBuilder.Только когда вы выполняете несколько операций и вам не нужны промежуточные результаты, которые StringBuilder сияют.

См. мою статью о StringBuilderдля получения дополнительной информации.

3 голосов
/ 29 июня 2011

Microsoft CLR выполняет некоторые операции с внутренним вызовом (не так, как небезопасный код). Наибольшее преимущество в производительности по сравнению с кучей + составных строк заключается в том, что он записывает в char[] и не создает столько промежуточных строк. Когда вы вызываете ToString (), он создает законченную неизменяемую строку из вашего содержимого.

1 голос
/ 29 июня 2011

StringBuilder использует строковый буфер, который можно изменить, по сравнению с обычным String, который не может быть. Когда вы вызываете ToString метод StringBuilder, он просто замораживает строковый буфер и преобразует его в обычную строку, поэтому ему не нужно копировать все данные один раз.

Поскольку StringBuilder может изменять строковый буфер, ему не нужно создавать новое строковое значение для каждого изменения строковых данных. Когда вы используете оператор +, компилятор превращает его в вызов String.Concat, который создает новый строковый объект. Это, казалось бы, невинный кусок кода:

str += ",";

компилируется в это:

str = String.Concat(str, ",");
...