C # Ускорение для строк? - PullRequest
       1

C # Ускорение для строк?

3 голосов
/ 05 февраля 2011
struct mydata
{
    public int id;
    public string data;
}

class Program
{
    static void Main(string[] args)
    {
        List<mydata> myc = new List<mydata>();

        Stopwatch stopwatch = new Stopwatch();

        stopwatch.Start();

        for (int i = 0; i < 1000000; i++)
        {
            mydata d = new mydata();

            d.id = i;
            d.data = string.Format("DataValue {0}",i);

            myc.Add(d);
        }

        stopwatch.Stop();
        Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
}

Почему этот код выше, так что МЕДЛЕННО ..?
На старом ноутбуке время: Код C # выше: 1500мс Подобный код в Delphi: 450ms ....

Затем я изменил код на KeyValue / Pair (см. Ниже):

</p>

<p>Stopwatch stopwatch = new Stopwatch();</p>

<pre><code>        stopwatch.Start();

        var list = new List<KeyValuePair<int , string>>();

        for (int i = 0; i < 1000000; i++)
        {
            list.Add(new KeyValuePair<int,string>(i, "DataValue" + i));
        }

        stopwatch.Stop();
        Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
        Console.ReadLine();

Это улучшило время до 1150 мс ..

Если я уберу '+ i', время будет <300 мс </p>

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

</p>

<pre><code>        StringBuilder sb = new StringBuilder();
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();

        var list = new List<KeyValuePair<int, string>>();

        for (int i = 0; i < 1000000; i++)
        {
            sb.Append("DataValue");
            sb.Append(i);
            list.Add(new KeyValuePair<int, string>(i, sb.ToString()));
            sb.Clear();
        }

        stopWatch.Stop();
        Console.WriteLine("End: {0}", stopWatch.ElapsedMilliseconds);
        Console.ReadLine();

Немного лучше .. Если вы удалите sb.Append (i), это очень быстро ..

Может показаться, что каждый раз, когда вам нужно добавить Int к строке / строителю строк, это ОЧЕНЬ МЕДЛЕННО.

Могу ли я ускорить это каким-либо образом ??

РЕДАКТИРОВАТЬ **

Приведенный ниже код является самым быстрым, который я могу получить после внесения предложений:

</p>

<p>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;</p>

<p>namespace ConsoleApplication1
{
    struct mydata
    {
        public int id;
        public string data;
    }</p>

<pre><code>class Program
{
    static void Main(string[] args)
    {
        List<mydata> myc = new List<mydata>();

        Stopwatch stopwatch = new Stopwatch();

        stopwatch.Start();

        for (int i = 0; i < 1000000; i++)
        {
           mydata d = new mydata();
           d.id = i;
           d.data = "DataValue " + i.ToString();
           myc.Add(d);
        }

        stopwatch.Stop();
        Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
        Console.ReadLine();
    }

}

}

Если я заменю строку:

<code>
  d.data = "DataValue " + i.ToString();
с:
<code>
  d.data = "DataValue ";

На моей домашней машине это составляет от 660 мс -> 31 мс ..

Да .. его на 630 мс медленнее с помощью '+ i.ToString ()'

Но все равно в 2 раза быстрее, чем boxing / string.format и т. Д.


            Stopwatch stopwatch = new Stopwatch();</p>

<pre><code>        stopwatch.Start();

        var list = new List<KeyValuePair<int, string>>();

        for (int i = 0; i < 1000000; i++)
        {
            list.Add(new KeyValuePair<int, string>(i, "DataValue" +i.ToString()));
        }

        stopwatch.Stop();
        Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
        Console.ReadLine();

равно 612 мс .. (нет разницы в скорости, если List> (1000000); предварительно инициализирован).

Ответы [ 4 ]

10 голосов
/ 05 февраля 2011

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

Например, в этой строке:

d.data = string.Format("DataValue {0}", i);

второй параметр для string.Format равен object, что вызывает бокс i. См. Код промежуточного языка для подтверждения этого:

...
box int32
call string [mscorlib]System.String::Format(string, object)
...

Аналогично этот код:

d.data = "DataValue " + i;

эквивалентно этому:

d.data = String.Concat("DataValue ", i);

При этом используется перегрузка String.Concat с параметрами типа object, так что опять-таки это включает в себя операцию упаковки. Это можно увидеть в сгенерированном коде промежуточного языка:

...
box int32
call string [mscorlib]System.String::Concat(object, object)
...

Для лучшей производительности этот подход позволяет избежать бокса:

d.data = "DataValue " + i.ToString();

Теперь код промежуточного языка не включает инструкцию box и использует перегрузку String.Concat, которая принимает две строки:

...
call instance string [mscorlib]System.Int32::ToString()
call string [mscorlib]System.String::Concat(string, string)
...
1 голос
/ 05 февраля 2011

На моей машине:

... String.Format("DataValue {0}", i ) // ~1650ms
... String.Format("DataValue {0}", "") // ~1250ms
... new MyData {Id = i, Data = "DataValue {0}" + i} // ~1200ms

Как сказал Марк, в операции участвует бокс.

В этом конкретном случае, когда вы получаете DataValue на основе своего идентификатора, вы можете создать свойство get или переопределить метод ToString () для выполнения этой операции именно тогда, когда вам это нужно.

public override string ToString()
{
    return "DataValue {0}" + Id;
}
0 голосов
/ 05 февраля 2011

Другим важным фактором снижения производительности в этом коде является список.Внутренне он хранит элементы в массиве.Когда вы вызываете Add, он проверяет, может ли новый Item помещаться в массив (EnsureCapacitiy).Когда ему нужно больше места, он создаст НОВЫЙ массив с удвоенным размером, а затем скопирует элементы из старого массива в новый.Вы можете увидеть все это, если посмотрите на List.Add в Reflector.

Таким образом, в вашем коде с 1 000 000 элементов необходимо скопировать массив примерно в 25 раз, и каждый раз он становится больше.

Если вы измените свой код на

var list = new List<KeyValuePair<int, string>>(1000000);

, вы увидите резкое увеличение скорости.Дайте нам знать, как вы поживаете!

С уважением,

GJ

0 голосов
/ 05 февраля 2011

Существует много вещей, которые могут повлиять на ваши результаты.Во-первых, ни одно из сделанных вами сравнений не равно.В обоих случаях у вас есть список и вы используете Add, то, что вы добавляете в список, не повлияет на время, изменение объявления List на var не повлияет на время.

Я не уверенпо аргументу бокса, выдвинутому Марком, это может быть проблемой, но я вполне уверен, что в первом случае есть неявный вызов .ToString.Это имеет свои собственные издержки и будет необходимо, даже если int упакован.

Форматирование - довольно дорогая операция.Вторая версия имеет конкатенацию строк, которая, вероятно, дешевле, чем .Format.

Третий просто дорог.Использование такого строителя строк неэффективно.Внутренне строитель строк - это просто список.Когда вы делаете .ToString, вы по существу делаете большую операцию concat.

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

Верно, вот мое предложение:

Первая версия, вероятно, ближайшая кбыть "правильным" в моем уме.Что вы могли бы сделать, это отложить часть обработки.Возьмите объект mydata и установите строковое свойство И свойство int.Только тогда, когда вам нужно прочитать строку, выведите результат через concat.Сохраните это, если вы собираетесь многократно повторять операцию печати.Это не обязательно будет быстрее, чем вы ожидаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...