Производительность работы со строками. Что я делаю не так?
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 21.12.16 19:08
Оценка: 22 (1)
По мотивам вот этого
Автор: Sinix
Дата: 19.04.16
поста.

  Код.
static void AssertState(bool condition, string message)
{
    if (!condition)
    {
        throw new InvalidOperationException(message);
    }
}

static void AssertState(bool condition, IFormattable message)
{
    if (!condition)
    {
        throw new InvalidOperationException(message.ToString());
    }
}

static void AssertStateMessage(bool condition, string message)
{
    if (!condition)
    {
        throw new ArgumentException(message);
    }
}

static void AssertStateMessage(bool condition, string messageFormat, params object[] args)
{
    if (!condition)
    {
        throw new ArgumentException(string.Format(messageFormat, args));
    }
}

static void AssertStateFunc(bool condition, Func<string> messageCallback)
{
    if (!condition)
    {
        throw new InvalidOperationException(messageCallback());
    }
}

static void Main(string[] args)
{
    const int Count = 10 * 1000 * 1000;
    Measure("Concatenation", () =>
    {
        for (int i = 0; i < Count; i++)
        {
            AssertState(true, "Message '" + i + "': " + i + "." + i + "!");
        }
        return Count;
    });
    Measure("Interpolation", () =>
    {
        for (int i = 0; i < Count; i++)
        {
            AssertState(true, $"Message '{i}':{i}.{i}!");
        }
        return Count;
    });
    Measure("String", () =>
    {
        for (int i = 0; i < Count; i++)
        {
            AssertState(true, "Message #0");
        }
        return Count;
    });
    Measure("String.Format", () =>
    {
        for (int i = 0; i < Count; i++)
        {
            AssertStateMessage(true, "Message '{0}':{0}.{0}!", i);
        }
        return Count;
    });

    Console.WriteLine();
    Console.WriteLine("Done.");
}

static void Measure(string name, Func<long> callback)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    var mem = GC.GetTotalMemory(true);
    var gc00 = GC.CollectionCount(0);
    var gc01 = GC.CollectionCount(1);
    var gc02 = GC.CollectionCount(2);

    var sw = System.Diagnostics.Stopwatch.StartNew();
    var result = callback();
    sw.Stop();

    var mem2 = GC.GetTotalMemory(false);
    var gc10 = GC.CollectionCount(0);
    var gc11 = GC.CollectionCount(1);
    var gc12 = GC.CollectionCount(2);

    var memDelta = (mem2 - mem) / 1024.0;
    var gcDelta0 = gc10 - gc00;
    var gcDelta1 = gc11 - gc01;
    var gcDelta2 = gc12 - gc02;

    Console.WriteLine(
        "{0,20}: {1,5}ms, ips: {2,22:N} | Mem: {3,9:N2} kb, GC 0/1/2: {4}/{5}/{6} => {7,6}",
        name, sw.ElapsedMilliseconds, result / sw.Elapsed.TotalSeconds, memDelta, gcDelta0, gcDelta1, gcDelta2, result);
}


Всё почти как в первоисточнике, разница лишь в том, что я делаю конкатенацию/форматирование с несколькими элементами, не с одним. Но результаты у меня получаются совершенно другие:

       Concatenation:  7545ms, ips:           1,325,367.80 | Mem:    955.69 kb, GC 0/1/2: 1351/0/0 => 10000000
       Interpolation:  7907ms, ips:           1,264,567.77 | Mem:    265.71 kb, GC 0/1/2: 968/0/0 => 10000000
              String:    58ms, ips:         170,960,920.04 | Mem:      8.00 kb, GC 0/1/2: 0/0/0 => 10000000
       String.Format:   303ms, ips:          32,982,759.58 | Mem:  1,058.69 kb, GC 0/1/2: 133/0/0 => 10000000

Интерполяция, получается, медленнее даже простой конкатенации. Проверял, собирая под разные фреймворки, от 4.0 до 4.6.2, плюс dotnet core, результаты примерно одинаковые.
Что я делаю не так?
С уважением, Artem Korneev.
Отредактировано 22.12.2016 2:04 VladD2 . Предыдущая версия . Еще …
Отредактировано 21.12.2016 23:06 Artem Korneev . Предыдущая версия .
Отредактировано 21.12.2016 23:05 Artem Korneev . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.