Оптимизация строковой операции в C#

Вы должны иметь возможность установить элемент управления обратно в пресестин: https://angular.io/api/forms/AbstractControl#markaspristine

Или использовать markAsUntouched () в качестве Jitendra G2. предложил.

7
задан Matthew Scharley 11 November 2008 в 23:56
поделиться

7 ответов

Вы будете, вероятно, видеть, что первые 1 000 символов не займут почти времени, настроенного против последних 1 000 символов.

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

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

Если бы Вы однако определили бы массив символов с соответствующим размером с начала, он, вероятно, сверкал бы быстро, потому что затем он не должен будет копировать его много раз.

7
ответ дан 6 December 2019 в 05:49
поделиться

Добавление символа к строке может иметь два последствия:

  • если существует все еще пространство для символа, это просто добавляется в конце; (как комментатор заметил, этого не может произойти со строками c#, поскольку ваш неизменны).
  • если нет никакого пространства в конце, новый блок памяти выделяется для новой строки, содержание старой строки копируется там, и символ добавляется.

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

Затем в ситуации, где никакое дополнительное пространство не резервируется, первый пример должен сделать 1 000 000 выделений и копий, в среднем 0.5 * 1 000 000 символов. Второй должен сделать 1 000 выделений и копий средних 0.5 * 1 000 000 символов, и 1 000 000 выделений и копий 0,5 * 1 000 символов. Если копирование является lineair с размером копии и бесплатного выделения, первая ситуация берет 500 000 000 000 единиц времени и вторую 500 000 000 + 500 000 000 единиц времени.

1
ответ дан 6 December 2019 в 05:49
поделиться

Кроме того, на связанную тему я услышал, что она сказала, что Вы никогда не должны использовать + оператор со строками, в пользу строки. Формат (), действительно ли это верно?

Нет, как все абсолютные операторы это не имеет смысла. Однако это верно то использование Format обычно делает форматирование код более читаемого, и это часто немного быстрее, чем конкатенация – но скорость не является решающим фактором здесь.

Что касается Вашего кода … это приводит к меньшим скопированным строкам (а именно, tmp) в конкатенации. Конечно, в fraction += tmp Вы копируете большую строку, но это происходит менее часто.

Поэтому Вы уменьшили много больших копий до некоторых больших и много маленьких копий.

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

3
ответ дан 6 December 2019 в 05:49
поделиться

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

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

// e.g. Initialise to 10MB
StringBuilder fraction = new StringBuilder(10000000);
8
ответ дан 6 December 2019 в 05:49
поделиться

Я не могу сделать тестов теперь, но попытаться использовать StringBuilder.

int i = 1;
    StringBuilder fraction = new StringBuilder();
    while (fraction.Length < 1000000)
    {
        fraction.Append(i);
        i++;
    }
return sb.ToString();
3
ответ дан 6 December 2019 в 05:49
поделиться

Ответьте на измененный вопрос ("почему делает эту неочевидную работу оптимизации, таким образом, хорошо" и "это верный, что Вы не должны использовать + оператор на строках"):

Я не уверен, о какой неочевидной оптимизации Вы говорите. Но ответ на второй вопрос, я думаю, касается всех оснований.

Путем строковая работа в C# состоит в том, что они выделяются как фиксированная длина и не могут быть изменены. Это означает, что любое время, Вы пытаетесь изменить длину строки, всей новой строки, создается, и старая строка копируется в до надлежащей длины. Это - очевидно, медленный процесс. Когда Вы используете Строку. Отформатируйте это внутренне использует StringBuilder для создания строки.

StringBuilders работают при помощи буфера памяти, который более разумно выделяется, чем строки фиксированной длины и таким образом работает значительно лучше в большинстве ситуаций. Я не уверен в деталях StringBuilder внутренне, таким образом, необходимо будет задать новый вопрос для этого. Я могу размышлять он, любой не перераспределяет старые части строки (вместо этого создающий связанный список внутренне и только на самом деле выделяющий окончательный результат при необходимости ToString), или это перераспределяет с экспоненциальным ростом (когда это исчерпывает память, это выделяет вдвое больше в следующий раз, таким образом для строки на 2 ГБ, которую это должно было бы только перераспределить приблизительно 30 раз).

Ваш пример с вложенными циклами растет линейно. это берет маленькую строку и выращивает тех до 1 000 и затем прикрепляет это 1000 на большей строке в одной большой операции. Поскольку большая строка становится действительно большой, копия, которая следует из создания новой строки, добирается для занимания много времени. При сокращении суммы времен, это сделано (вместо этого изменяя размер меньшей строки чаще вместо этого), Вы увеличиваете скорость. Конечно, StringBuilder еще более умен о выделении памяти и таким образом намного быстрее.

1
ответ дан 6 December 2019 в 05:49
поделиться

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

using System;
using System.Diagnostics;
using System.Text;

public class Test
{
    const int Limit = 4000000;

    static void Main()
    {
        Time(Concatenation, "Concat");
        Time(SimpleStringBuilder, "StringBuilder as in post");
        Time(SimpleStringBuilderNoToString, "StringBuilder calling Append(i)");
        Time(CapacityStringBuilder, "StringBuilder with appropriate capacity");
    }

    static void Time(Action action, string name)
    {
        Stopwatch sw = Stopwatch.StartNew();
        action();
        sw.Stop();
        Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    static void Concatenation()
    {
        int i = 1;
        string fraction = "";
        while (fraction.Length < Limit)
        {
            // concatenating strings is much faster for small strings
            string tmp = "";
            for (int j = 0; j < 1000; j++)
            {
                tmp += i.ToString();
                i++;
            }
            fraction += tmp;            
        }
    }

    static void SimpleStringBuilder()
    {
        int i = 1;
        StringBuilder fraction = new StringBuilder();
        while (fraction.Length < Limit)
        {
            fraction.Append(i.ToString());
            i++;
        }
    }

    static void SimpleStringBuilderNoToString()
    {
        int i = 1;
        StringBuilder fraction = new StringBuilder();
        while (fraction.Length < Limit)
        {
            fraction.Append(i);
            i++;
        }
    }

    static void CapacityStringBuilder()
    {
        int i = 1;
        StringBuilder fraction = new StringBuilder(Limit + 10);
        while (fraction.Length < Limit)
        {
            fraction.Append(i);
            i++;
        }
    }
}

И результаты:

Concat: 5879ms
StringBuilder as in post: 206ms
StringBuilder calling Append(i): 196ms
StringBuilder with appropriate capacity: 184ms

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

9
ответ дан 6 December 2019 в 05:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: