Страницы

Поиск по вопросам

среда, 18 декабря 2019 г.

StringBuilder vs. String.Concat, C# [дубликат]

#c_sharp #net #строки


        
             
                
                    
                        
                            This question already has an answer here:
                            
                        
                    
                
                        
                            Быстродействие объединения строк
                                
                                    (1 ответ)
                                
                        
                                Закрыт 2 года назад.
            
                    
Update: Мои тесты были неправильные. StringBuilder выигрывает даже при объединении
5 строк

Я сравнил производительность StringBuilder и String.Concat c разным количеством строк,
учитывая время на создание объекта StringBuilder.

При объединении 5 строк String.Concat всегда быстрее, иногда в 10 раз


При объедении 10 строк String.Concat всегда быстрее, иногда в 10 раз


При объединении 20 строк String.Concat всегда быстрее, иногда в 10 раз 


Если ли смысл использовать StringBuilder для объединения маленького количества строк?
Вроде объект StringBuilder сам по себе отнимает больше ресурсов, чем 20 строк.

Код, который использовался для теста:

class Program
{
    static void Main(string[] args)
    {
        const int countOfStrings = 20;
        string[] stringsToMerge = new string[countOfStrings];

        for (int i = 0; i < countOfStrings; i++)
        {
            stringsToMerge[i] = "stringN" + i;
        }

        Console.WriteLine("Count of strings:" + countOfStrings);

        Stopwatch stopWatch = new Stopwatch();
        TimeSpan ts;
        stopWatch.Start();
        stopWatch.Stop();

        stopWatch.Restart();
        string s = stringsToMerge[0];
        for (int i = 1; i < countOfStrings; i++)
        {
            s = string.Concat(s, stringsToMerge[i]);
        }
        stopWatch.Stop();
        ts = stopWatch.Elapsed;
        Console.WriteLine("String.Concat ms:" + ts.TotalMilliseconds);

        stopWatch.Restart();
        StringBuilder stringBuilder = new StringBuilder(stringsToMerge[0]);
        for (int i = 1; i < countOfStrings; i++)
        {
            stringBuilder.Append(stringsToMerge[i]);
        }
        stopWatch.Stop();
        ts = stopWatch.Elapsed;
        Console.WriteLine("StringBuilder ms:" + ts.TotalMilliseconds);

        Console.ReadKey();

    }
}

    


Ответы

Ответ 1



Я исправил ваш код. Во-первых, перед тестированием скорости, все методы нужно "прогреть". При первом вызове JIT-компилятор генерирует машинный код с IL и это занимает некоторое время. Во-вторых, не нужно тестировать одну итерацию, так как случайное событие в ОС может исказить результат. Во-третьих, вы передаете странное значение, в конструктор StringBuilder. При его использовании заведомо создается маленький размер буфера, который будет расширяться несколько раз. Для максимальной производительности, если есть возможность, вы должны задавать размер буфера, который не будет менятся. И не забывайте запускать в Release, а не в Debug. В исправленном варианте (не трогал конструктор): Count of strings:5 String.Concat ms:1097.4772 StringBuilder ms:1294.881 Count of strings:10 String.Concat ms:2848.4845 StringBuilder ms:1970.4477 Уже на 10 выигрывает StringBuilder. const int countOfStrings = 5; const int iterations = 10000000; string[] stringsToMerge = new string[countOfStrings]; for (int i = 0; i < countOfStrings; i++) { stringsToMerge[i] = "stringN" + i; } // компилируем IL в машинный код string.Concat("123", "qwe"); var x = new StringBuilder(40); x.Append("123qwe"); Console.WriteLine("Count of strings:" + countOfStrings); Stopwatch stopWatch = new Stopwatch(); TimeSpan ts; stopWatch.Start(); stopWatch.Stop(); stopWatch.Restart(); for (int iter = 0; iter < iterations; iter++) { string s = stringsToMerge[0]; for (int i = 1; i < countOfStrings; i++) { s = string.Concat(s, stringsToMerge[i]); } } stopWatch.Stop(); ts = stopWatch.Elapsed; Console.WriteLine("String.Concat ms:" + ts.TotalMilliseconds); stopWatch.Restart(); for (int iter = 0; iter < iterations; iter++) { StringBuilder stringBuilder = new StringBuilder(stringsToMerge[0]); for (int i = 1; i < countOfStrings; i++) { stringBuilder.Append(stringsToMerge[i]); } } stopWatch.Stop(); ts = stopWatch.Elapsed; Console.WriteLine("StringBuilder ms:" + ts.TotalMilliseconds); Если же я переделаю использование StringBuilder, то он выигрывает уже на 5 строках: StringBuilder stringBuilder = new StringBuilder(100); for (int i = 0; i < countOfStrings; i++) { stringBuilder.Append(stringsToMerge[i]); } Count of strings:5 String.Concat ms:1053.0082 StringBuilder ms:699.7725

Ответ 2



Даже если не принимать во внимание неправильность тестов - пусть вы провели более тщательное тестирование и выяснили, например, что на 10 строках результат следующий Count of strings:10 String.Concat ms:2848.4845 StringBuilder ms:1970.4477 Какой вывод вы из этого делаете? StringBuilder выигрывает даже при объединении 5 строк Какой реальный вывод стоит сделать из таких результатов? Нет ощутимой разницы между StringBuilder и String.Concat в случаях, если ваша программа не занимается исключительно сложением миллиардов строк. Во всех остальных случаях вы не заметите разницы. Потому что разница на одной операции - 0.00000009 секунд. Более того, если разница между StringBuilder и String.Concat на одном сложении для вас значительна - значит вы выбрали неправильную платформу и неправильный язык. Пишите на чем-нибудь более низкоуровневом, что даст вам максимальную производительность.

Ответ 3



Вы преследуете неправильную цель. Не стоит заниматься микрооптимизациями, компилятор делает это сейчас или будет делать в будущем лучше. Например, string.Concat имеет больше информации о том, что именно вы хотите сделать, поэтому он может быть как минимум настолько же быстрым, как и использование StringBuilder'а в цикле. Если он сейчас, на вашей платформе, на вашей версии JIT-компилятора медленнее, это ничего не значит, и может измениться в близком будущем. Вот, например, (неполный) список улучшений производительности, введённых недавно в .NET Core 2: Queue<>.Enqueue и Dequeue ускорено вдвое SortedSet<> ускорен в 600 раз List<>.Add ускорен в 1.3 раза ConcurrentQueue<> ускорена в 3.5 раза string.StartsWith ускорено вдвое и так далее. Таким образом, измерения мелкой производительности BCL релевантны только на той версии библиотеки и в тот момент, когда вы их проводите. Оптимизируйте лучше алгоритмы. Прирост производительности на этом будет гораздо больше, чем выцеживание наносекунд из базовых структур данных.

Ответ 4



Я хотел сделать заметку , хочу сказать о памяти (этот вопрос Вообще задаются в экзамене 70-483 Programming in C#). 'string' (immutable) является неизменен, непреложный означает, что если вы создаете строковый объект, то вы не можете его изменить и всегда создаете новый объект типа 'string' в памяти. 'StringBuilder' (mutable) является измени мой (То есть он не создает новый объект), означает что если вы создаете объект 'StringBuilder', вы можете выполнять любую операцию, например вставку, замену или добавление, без создания нового экземпляра для каждый раз. он обновит строку в одном месте в памяти, не создаст новое пространство в памяти. То есть 'string.Concat()' он создаёт каждый раз новый объект 'string'. это влияет на память потому что 'string' (immutable) является неизменен!

Комментариев нет:

Отправить комментарий