В какой момент использование StringBuilder становится незначительным или накладными? - PullRequest
17 голосов
/ 15 февраля 2009

Недавно я обнаружил, что использую StringBuilder для всех конкатенаций строк, больших и маленьких, однако в недавнем тесте производительности я поменял коллегу на stringOut = string1 + "." Конкатенация в стиле string2 (используется в цикле 10000x + с StringBuilder, который каждый раз обновляется) для StringBuilder, просто чтобы увидеть, как это изменится в незначительной конкатенации.

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

В какой момент «обновление» объекта StringBuilder сводит на нет преимущества его использования?

Ответы [ 7 ]

20 голосов
/ 15 февраля 2009

Правило, которому я следую, -

Используйте StringBuilder, когда число конкатенаций неизвестно во время компиляции.

Итак, в вашем случае каждый StringBuilder добавлялся только несколько раз, а затем отбрасывался. Это не то же самое, что

string s = String.Empty;
for (int i = 0; i < 10000; ++i)
{
    s += "A";
}

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

18 голосов
/ 15 февраля 2009

Я уверен, что у меня есть другой ответ, где я разместил только ссылку на мою статью и затем ее резюме, но здесь мы снова.

  • Определенно используйте StringBuilder, когда вы объединяете в нетривиальном цикле - особенно если вы не знаете наверняка (во время компиляции), сколько итераций вы сделаете в цикле. Например, чтение файла по символу за раз, построение строки при использовании оператора + = потенциально может привести к самоубийству.

  • Определенно используйте оператор конкатенации, когда вы можете (читаемо) указать все, что необходимо объединить, в одном выражении. (Если у вас есть множество вещей для объединения, рассмотрите возможность вызова String.Concat явно - или String.Join, если вам нужен разделитель.)

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

  • Если вам нужны промежуточные результаты конкатенации для чего-то другого, кроме подачи следующей итерации конкатенации, StringBuilder вам не поможет. Например, если вы создадите полное имя из имени и фамилии, а затем добавите в конец третий фрагмент информации (возможно, псевдоним), вы сможете воспользоваться StringBuilder, только если вы не нам не нужна строка (имя + фамилия) для других целей (как мы делаем в примере, который создает объект Person).

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

6 голосов
/ 15 февраля 2009

Иногда стоит посмотреть документацию :

Производительность конкатенации операция для строки или Объект StringBuilder зависит от того, как часто происходит выделение памяти. Операция конкатенации строк всегда выделяет память, тогда как Операция конкатенации StringBuilder только выделяет память, если Буфер объекта StringBuilder слишком маленький для размещения новых данных. Следовательно, класс String предпочтительнее для объединения операция, если фиксированное число строк объекты объединены. В этом случай, индивидуальная конкатенация операции могут быть даже объединены в одна операция компилятора. Объект StringBuilder предпочтительнее для операция конкатенации, если произвольное количество строк сцеплены; например, если цикл объединяет случайное число строки ввода пользователя.

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

stringOut = ...
for(...)
    stringOut += "."
    stringOut += string2
2 голосов
/ 15 февраля 2009

Мое простое правило.

  1. Если вы разумно можете написать одно выражение, производящее финал, тогда используйте +.
  2. Если вы не можете (из-за размера или изменчивости), используйте StringBuilder.

По моему опыту, такие выражения, как:

"Id: " + item.id + " name: " + item.name

можно написать и понять гораздо легче, чем:

StringBuilder sb = new StringBuilder();
sb.append("Id: ").append(item.id);
sb.append(" name: ").append(item.name);

(с последующим использованием строки из sb, где было бы написано вышеприведенное выражение), и она работает одинаково хорошо (подсказка: посмотрите на скомпилированный код, чтобы понять почему!)

С другой стороны, когда необходимо накапливать строку во времени (во время выполнения программы) или в пространстве (состоящем из значений, поступающих из разных частей кода), таким образом, что нецелесообразно писать как один строковое выражение, то StringBuilder избегает накладных расходов (времени и памяти):

String s = somethingExpression;
...
s += someOtherExpression;
...
s += yetAnotherExpression;
...
1 голос
/ 15 февраля 2009

С Dot Net Perls :

Когда использовать StringBuilder?

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

Иногда можно использовать небольшие циклы из 4 и менее итераций с простыми строковыми конкататами. Однако в крайних случаях это может иметь катастрофические последствия. Планируйте свои крайние случаи с StringBuilder.

1 голос
/ 15 февраля 2009

Об этом есть интересная статья на Coding Horror . Джефф получил следующие результаты для 100 000 итераций на двухъядерном Core 2 Duo с частотой 3,5 ГГц:

 Simple Concatenation    - 606 ms
 String.Format           - 665 ms
 string.Concat           - 587 ms
 String.Replace          - 979 ms
 StringBuilder           - 588 ms
1 голос
/ 15 февраля 2009

С MSDN :

[T] класс String предпочтительнее для операция конкатенации, если фиксированная количество объектов String сцепляются. В этом случае индивидуальные операции конкатенации может даже быть объединен в один работа компилятором. Объект StringBuilder предпочтительнее для операция конкатенации, если произвольное количество строк сцеплены; например, если цикл объединяет случайное число строки ввода пользователя.

Я полагаю, что ответ "это зависит" - если вы объединяете внутри цикла с более чем несколькими итерациями, тогда StringBuilder почти всегда будет обеспечивать лучшую производительность, но единственный способ узнать наверняка - это профилировать .

...