2008-08-09 2 views
144

Предположим, у меня есть StringBuilder в C#, который делает это:ли String.Format так эффективно, как StringBuilder

StringBuilder sb = new StringBuilder(); 
string cat = "cat"; 
sb.Append("the ").Append(cat).(" in the hat"); 
string s = sb.ToString(); 

это будет столь же эффективным или любой более эффективным, как имеющие:

string cat = "cat"; 
string s = String.Format("The {0} in the hat", cat); 

Если так , Зачем?

EDIT

После некоторых интересных ответов я понял, что я, вероятно, должен был немного яснее, в чем я спрашивал. Я не столько просил, чтобы ускорить конкатенирование струны, но быстрее на впрыскивание одна строка в другую.

В обоих случаях выше Я хочу вставить одну или несколько строк в середину предопределенной строки шаблона.

Извините за сумбур

+0

Пожалуйста, оставьте их открытыми для обеспечения будущих улучшений. – 2008-09-23 17:52:45

+3

В сценарии с особым случаем самый быстрый из них не является ни тем, ни другим: если подлежащая замене часть равна размеру новой части, вы можете изменить строку на месте. К сожалению, для этого требуется отражение или небезопасный код и сознательно нарушает неизменность строки. Не хорошая практика, но если скорость - проблема ... :) – Abel 2009-11-04 12:53:59

+0

в приведенном выше примере `` `string s =" «+ cat +» в шляпе »;` `` может быть самым быстрым, если только он не используется в цикле, и в этом случае быстрее всего будет с `` `` StringBuilder `` `` инициализирован вне цикла. – 2016-09-24 10:12:48

ответ

136

String.Format использует StringBuilder внутренне:

public static string Format(IFormatProvider provider, string format, params object[] args) 
{ 
    if ((format == null) || (args == null)) 
    { 
     throw new ArgumentNullException((format == null) ? "format" : "args"); 
    } 

    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8)); 
    builder.AppendFormat(provider, format, args); 
    return builder.ToString(); 
} 

Приведенный выше код представляет собой фрагмент из mscorlib, поэтому возникает вопрос «является StringBuilder.Append() быстрее, чем StringBuilder.AppendFormat()»?

Без бенчмаркинга Я бы сказал, что пример кода выше будет работать быстрее, используя .Append(). Но это предположение, попробуйте сравнить и/или профилировать два, чтобы получить правильное сравнение.

Этот парень, Джерри Диксон, сделал некоторые бенчмаркинг:

http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Обновлено:

К сожалению, ссылка выше уже умер. Однако есть еще копия на машине Путь Назад:

http://web.archive.org/web/20090417100252/http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

В конце концов это зависит, собирается ли ваша строка форматирования будет называться повторно, т.е. вы делаете некоторые серьезные текстовую обработку более 100 мегабайт текста, или она вызывается, когда пользователь нажимает кнопку снова и снова. Если вы не выполняете какое-то огромное задание пакетной обработки, я бы придерживался String.Format, это облегчает читаемость кода. Если вы подозреваете, что это узкое место, тогда приклейте профайлер на свой код и посмотрите, где он на самом деле.

+6

Одна из проблем с эталонами на странице Джерри Диксона заключается в том, что он никогда не называет `.ToString()` на объекте `StringBuilder`. В течение многих итераций это время имеет большое значение и означает, что он не совсем сравнивает яблоки с яблоками. Именно по этой причине он демонстрирует такую ​​отличную производительность для «StringBuilder» и, вероятно, объясняет его удивление. Я только что повторил бенчмарк, исправляющий эту ошибку, и получил ожидаемые результаты: оператор `String`` + `был самым быстрым, за ним последовал` StringBuilder`, с `String.Format`, восходящим сзади. – 2013-07-19 21:28:17

+5

6 лет спустя, это уже не так. В Net4 string.Format() создает и кэширует экземпляр StringBuilder, который он использует повторно, поэтому в некоторых тестовых случаях он может быть быстрее, чем StringBuilder. Я поставил пересмотренный тест в ответе ниже (который все еще говорит о том, что concat является самым быстрым и для моего теста, формат на 10% медленнее, чем StringBuilder). – 2015-01-12 12:28:50

-2

Я хотел бы предложить, нет, поскольку String.Format не предназначен для конкатенации, это дизайн для форматирования вывода различных входов, таких как дата.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today); 
0

На самом деле это зависит. Для небольших строк с несколькими конкатенациями на самом деле быстрее просто добавить строки.

String s = "String A" + "String B"; 

Но для большей строки (очень очень больших строк), то тогда более эффективно использовать StringBuilder.

43

От MSDN documentation:

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

8

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

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

С другой стороны, если вы говорите о большом фрагменте статического текста с двумя или тремя переменными в нем, даже если он немного менее эффективен, я думаю, что ясность, которую вы получаете от string.Format, стоит того Это. Я использовал это ранее на этой неделе, когда нужно разместить один бит динамического текста в центре документа на 4 страницы. Будет легче обновить этот большой кусок текста, если его в одном фрагменте, чем обновление трех частей, которые вы объедините вместе.

+0

Да! Используйте String.Format, когда имеет смысл это сделать, т. Е. Когда вы форматируете строки. Используйте конкатенацию строк или StringBuilder, когда вы выполняете механическую конкатенацию. Всегда старайтесь выбирать метод, который передает ваше намерение следующему сопровождающему. – Rob 2009-05-23 21:40:06

-1

В обоих случаях выше Я хочу ввести одну или несколько строк в середину предопределенной строки шаблона.

В этом случае я бы предложил String.Format является самым быстрым, потому что он предназначен для этой цели.

9

Я ожидал бы String.Format медленнее - он должен разобрать строку и затем сцепить его.

Пара примечаний:

  • Формат это путь для видимых пользователю строк в профессиональных приложениях; это позволяет избежать локализации ошибок
  • Если вы знаете длину результирующей строки заранее, используйте StringBuilder (Int32) конструктор предопределить Емкость
11

Я провел несколько быстрых тестов производительности, а также для 100000 операций, усредненное по 10 запусков, первый метод (String Builder) занимает почти половину времени второго (String Format).

Итак, если это нечасто, это не имеет значения. Но если это обычная операция, вы можете использовать первый метод.

3

О также, быстро будет:

string cat = "cat"; 
string s = "The " + cat + " in the hat"; 
5

String.Format использует StringBuilder внутри ... так логично, что приводит к тому, что она будет немного меньше производительным за счет более накладных расходов. Однако простая конкатенация строк - это самый быстрый способ впрыска одной строки между двумя другими ... в значительной степени. Это доказательство было продемонстрировано Рико Мариани в его первой спектакле «Викторина», много лет назад. Простым фактом является то, что конкатенации ... когда количество деталей струны известно (без ограничений .. вы можете объединить тысячу частей ... пока вы знаете, что это всегда 1000 частей) ... всегда быстрее, чем StringBuilder или String .Формат. Они могут выполняться с единичным распределением памяти в виде серии копий памяти. Here доказательство

А вот фактический код для некоторых методов String.Concat, которые в конечном счете называют FillStringChecked, которая использует указатели для копирования памяти (извлеченный с помощью рефлектора):

public static string Concat(params string[] values) 
{ 
    int totalLength = 0; 

    if (values == null) 
    { 
     throw new ArgumentNullException("values"); 
    } 

    string[] strArray = new string[values.Length]; 

    for (int i = 0; i < values.Length; i++) 
    { 
     string str = values[i]; 
     strArray[i] = (str == null) ? Empty : str; 
     totalLength += strArray[i].Length; 

     if (totalLength < 0) 
     { 
      throw new OutOfMemoryException(); 
     } 
    } 

    return ConcatArray(strArray, totalLength); 
} 

public static string Concat(string str0, string str1, string str2, string str3) 
{ 
    if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null))) 
    { 
     return Empty; 
    } 

    if (str0 == null) 
    { 
     str0 = Empty; 
    } 

    if (str1 == null) 
    { 
     str1 = Empty; 
    } 

    if (str2 == null) 
    { 
     str2 = Empty; 
    } 

    if (str3 == null) 
    { 
     str3 = Empty; 
    } 

    int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length; 
    string dest = FastAllocateString(length); 
    FillStringChecked(dest, 0, str0); 
    FillStringChecked(dest, str0.Length, str1); 
    FillStringChecked(dest, str0.Length + str1.Length, str2); 
    FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3); 
    return dest; 
} 

private static string ConcatArray(string[] values, int totalLength) 
{ 
    string dest = FastAllocateString(totalLength); 
    int destPos = 0; 

    for (int i = 0; i < values.Length; i++) 
    { 
     FillStringChecked(dest, destPos, values[i]); 
     destPos += values[i].Length; 
    } 

    return dest; 
} 

private static unsafe void FillStringChecked(string dest, int destPos, string src) 
{ 
    int length = src.Length; 

    if (length > (dest.Length - destPos)) 
    { 
     throw new IndexOutOfRangeException(); 
    } 

    fixed (char* chRef = &dest.m_firstChar) 
    { 
     fixed (char* chRef2 = &src.m_firstChar) 
     { 
      wstrcpy(chRef + destPos, chRef2, length); 
     } 
    } 
} 

Итак:

string what = "cat"; 
string inthehat = "The " + what + " in the hat!"; 

Наслаждайтесь!

6

Если только потому, что string.Format точно не делать то, что вы могли бы подумать, здесь перекладки испытаний 6 лет спустя на Net45.

Concat все еще быстрее, но на самом деле разница составляет менее 30%. StringBuilder и Format отличаются только 5-10%. Я несколько раз тестировал 20% тестов.

миллисекунды, миллион итераций:

  • конкатенация: 367
  • Новый StringBuilder для каждого ключа: 452
  • Cached StringBuilder: 419
  • строка.Формат: 475

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

const int iterations=1000000; 
var keyprefix= this.GetType().FullName; 
var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations); 
Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength); 

var concatkeys= new string[iterations]; 
var stringbuilderkeys= new string[iterations]; 
var cachedsbkeys= new string[iterations]; 
var formatkeys= new string[iterations]; 

var stopwatch= new System.Diagnostics.Stopwatch(); 
Console.WriteLine("Concatenation:"); 
stopwatch.Start(); 

for(int i=0; i<iterations; i++){ 
    var key1= keyprefix+":" + i.ToString(); 
    concatkeys[i]=key1; 
} 

Console.WriteLine(stopwatch.ElapsedMilliseconds); 

Console.WriteLine("New stringBuilder for each key:"); 
stopwatch.Restart(); 

for(int i=0; i<iterations; i++){ 
    var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString(); 
    stringbuilderkeys[i]= key2; 
} 

Console.WriteLine(stopwatch.ElapsedMilliseconds); 

Console.WriteLine("Cached StringBuilder:"); 
var cachedSB= new StringBuilder(maxkeylength); 
stopwatch.Restart(); 

for(int i=0; i<iterations; i++){ 
    var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString(); 
    cachedsbkeys[i]= key2b; 
} 

Console.WriteLine(stopwatch.ElapsedMilliseconds); 

Console.WriteLine("string.Format"); 
stopwatch.Restart(); 

for(int i=0; i<iterations; i++){ 
    var key3= string.Format("{0}:{1}", keyprefix,i.ToString()); 
    formatkeys[i]= key3; 
} 

Console.WriteLine(stopwatch.ElapsedMilliseconds); 

var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-'); 
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway); 

 Смежные вопросы

  • Нет связанных вопросов^_^