Это Строка.Формат так же эффективен, как StringBuilder



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



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



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



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



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

597   12  

12 ответов:

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, поэтому вопрос становится " is 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 мегабайт текста, или он вызывается, когда пользователь нажимает кнопку время от времени. Если вы не делаете какую-то огромную работу пакетной обработки Я бы держался за веревочку.Формат, он помогает читабельности кода. Если вы подозреваете узкое место perf, то вставьте профилировщик в свой код и посмотрите, где он действительно находится.

с Примечание:

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

Я провел несколько быстрых тестов производительности, и для 100 000 операций, усредненных за 10 запусков, первый метод (строковый конструктор) занимает почти половину времени второго (строковый формат).

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

Я бы ожидал строку.Формат медленнее - он должен разобрать строку и затем добавил его.

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

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

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

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

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

Concat по-прежнему самый быстрый, но на самом деле это менее 30% разницы. StringBuilder и формат отличаются всего на 5-10%. Я получил вариации 20%, запустив тесты несколько раз.

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

  • конкатенация: 367
  • новый stringBuilder для каждого ключа: 452
  • кэшированные 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);

строку.Формат использует StringBuilder internally...so логически это приводит к идее, что он будет немного менее производительным из-за больших накладных расходов. Однако простая конкатенация строк является самым быстрым способом введения одной строки между двумя others...by в значительной степени. Это доказательство было продемонстрировано Рико Мариани в его самой первой викторине производительности, много лет назад. Простой факт заключается в том, что конкатенации...когда количество частей строки известно (без ограничения..вы могли бы объединить тысяча частей...так долго, как вы знаете, его всегда 1000 деталей)...всегда быстрее, чем StringBuilder или 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!";

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

о ТАКЖЕ, Самый быстрый будет:

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

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

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

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

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

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

Это действительно зависит от вашей модели использования.
Подробный тест между string.Join,string,Concat и string.Format можно найти здесь: строку.Формат не подходит для интенсивного ведения журнала

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

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

Comments

    Ничего не найдено.