Информация об изменениях

Сообщение Re[2]: dotnetBenchmark как готовить от 07.07.2020 17:41

Изменено 07.07.2020 19:56 okon

Re[2]: dotnetBenchmark как готовить
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, okon, Вы писали:


O>>Хотелось померять скорость вставки строки в разных сценариях с обычной строкой, билдер без выделенного заранее буффера и билдер с буффером.

O>>Если не настраивать параметры теста, то по умолчанию он начинает длинные танцы с бубном, я так и не дождался результата за 5 минут и не понятно когда ждать завершения — прервал.

S>Не очень понятно, что именно вы хотите измерить.

S>BDN предполагает, что замеряемая операция воспроизводима.

Хотелось задать строку определенной длины например 100 символов и посмотреть сколько будет стоить вставка строки в 1 символ.

S>Т.е. что-то типа "берём пустой stringbuilder и вставляем в него 100 символов по одному", сравниваем с "берём stringbuilder с capacity=100 и вставляем в него 100 символов по одному".


S>То, что у вас написано — это стрингбилдер инициализируется 1 раз, затем в него охулиард раз вставляется 1 символ.

Во как, я просто привык что в тестах обычно на каждый вызов метод создается новый экземпляр.
Тут видимо иначе, в документации я нашел пример с конструктором, но там не уточняется как этот экземпляр используется.
https://benchmarkdotnet.org/articles/overview.html

p.s. проверил экспериментально — да конструктор 1 раз вызывается на все замеры а не перед каждым замером, печально придется как-то обходить.


S>Понятно, что перформанс этой вставки зависит не от подробностей инициализации, а от посторонних вещей — типа пришлось ли сделать GC при перевыделении буфера во время именно этой вставки.



S>Я бы писал примерно так:

S>[cs]

S> [Benchmark]

S> public void InsertStringBuilderWithCapacity()
S> {
S> var stringBuilderWithCapacity = new StringBuilder(BaseStr.Length * RepeatCount + InsertCount);
S> stringBuilderWithCapacity.Append(GetString());
S> for(var i=0; i<InsertCount; i++)
S> stringBuilderWithCapacity.Insert(4, "5");
S> }


Да но в этом случае тест получается не совсем корректный — тут мы измеряем не только вставку но и создание экземпляра StringBuilder и копирование в него строки.
А хочется замерить только вставку в уже проинициализированный. Можно конечно создать дополнительные методы без вставки и сравнить время, но это как временный вариант — не уверен что это правильный способ.

p.s. попробовал сделать замер с методами инициализации и вставки получается тоже высокая погрешность и странности например метод InsertStringBuilder () меньше погрешность чем у метода InitStringBuilder (), хотя insert состоит из 2х частей такой же init и непосредственно операция вставки. Но есть и плюсы — метод Empty стал адекватнее измеряться.

|                          Method |      Job | InvocationCount | IterationCount | UnrollFactor | RepeatCount | BaseString |        Mean |         Error |      StdDev |      Median |
|-------------------------------- |--------- |---------------- |--------------- |------------- |------------ |----------- |------------:|--------------:|------------:|------------:|
|                       InsertStr | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 388.3000 ns |    80.3761 ns |  20.8734 ns | 398.5000 ns |
|                         InitStr | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 343.0000 ns |    94.7524 ns |  24.6069 ns | 342.0000 ns |
|             InsertStringBuilder | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 504.2000 ns |   246.6427 ns |  64.0523 ns | 497.0000 ns |
|               InitStringBuilder | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 691.1000 ns | 1,509.4735 ns | 392.0055 ns | 421.5000 ns |
| InsertStringBuilderWithCapacity | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 530.5000 ns |   322.6899 ns |  49.9366 ns | 516.5000 ns |
|   InitStringBuilderWithCapacity | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 536.0000 ns |   462.2056 ns | 120.0333 ns | 525.0000 ns |
|                           Empty | QuickJob |             100 |              5 |            1 |          10 | 1234567890 |   0.0000 ns |     0.0000 ns |   0.0000 ns |   0.0000 ns |
|                       InsertStr | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 277.5542 ns |   142.1498 ns |   7.7917 ns | 275.7582 ns |
|                         InitStr | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 240.7382 ns |   217.4022 ns |  11.9165 ns | 236.3251 ns |
|             InsertStringBuilder | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 354.2914 ns |   182.9307 ns |  10.0270 ns | 359.9009 ns |
|               InitStringBuilder | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 291.1609 ns |   253.5002 ns |  13.8952 ns | 285.7806 ns |
| InsertStringBuilderWithCapacity | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 339.9996 ns |    35.9658 ns |   1.9714 ns | 340.9990 ns |
|   InitStringBuilderWithCapacity | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 293.8455 ns |   112.6605 ns |   6.1753 ns | 291.8657 ns |
|                           Empty | ShortRun |               1 |              3 |           16 |          10 | 1234567890 |   0.0013 ns |     0.0216 ns |   0.0012 ns |   0.0015 ns |


  код бенчмарка
   [SimpleJob(launchCount: 1, warmupCount: 3, targetCount: 5, invocationCount: 100, id: "QuickJob")]
    [ShortRunJob]
    public class BenchmarkInsertCharTest
    {
            [Params(10)]
            public int RepeatCount { get; set; }
            [Params("1234567890")]
            public string BaseString { get; set; }

            [Benchmark]
            public void InsertStr()
            {
                var str = GetString();
                str = str.Insert(4, "5");
            }

            [Benchmark]
            public void InitStr()
            {
                var str = GetString();
            }

            [Benchmark]
            public void InsertStringBuilder()
            {
                var stringBuilder = new StringBuilder(GetString());
                stringBuilder.Insert(4, "5");
            }

            [Benchmark]
            public void InitStringBuilder()
            {
                var stringBuilder = new StringBuilder(GetString());
            }


            [Benchmark]
            public void InsertStringBuilderWithCapacity()
            {
                var stringBuilderWithCapacity = new StringBuilder(BaseString.Length * (RepeatCount + 1));
                stringBuilderWithCapacity.Append(GetString());
                stringBuilderWithCapacity.Insert(4, "5");
            }

            [Benchmark]
            public void InitStringBuilderWithCapacity()
            {
                var stringBuilderWithCapacity = new StringBuilder(BaseString.Length * (RepeatCount + 1));
                stringBuilderWithCapacity.Append(GetString());
            }


            [Benchmark]
            public void Empty()
            {
            }

            private string GetString() => string.Join(string.Empty, Enumerable.Repeat(BaseString, RepeatCount));
    }
Re[2]: dotnetBenchmark как готовить
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, okon, Вы писали:


O>>Хотелось померять скорость вставки строки в разных сценариях с обычной строкой, билдер без выделенного заранее буффера и билдер с буффером.

O>>Если не настраивать параметры теста, то по умолчанию он начинает длинные танцы с бубном, я так и не дождался результата за 5 минут и не понятно когда ждать завершения — прервал.

S>Не очень понятно, что именно вы хотите измерить.

S>BDN предполагает, что замеряемая операция воспроизводима.

Хотелось задать строку определенной длины например 100 символов и посмотреть сколько будет стоить вставка строки в 1 символ.

S>Т.е. что-то типа "берём пустой stringbuilder и вставляем в него 100 символов по одному", сравниваем с "берём stringbuilder с capacity=100 и вставляем в него 100 символов по одному".


S>То, что у вас написано — это стрингбилдер инициализируется 1 раз, затем в него охулиард раз вставляется 1 символ.

Во как, я просто привык что в тестах обычно на каждый вызов метод создается новый экземпляр.
Тут видимо иначе, в документации я нашел пример с конструктором, но там не уточняется как этот экземпляр используется.
https://benchmarkdotnet.org/articles/overview.html

p.s. проверил экспериментально — да конструктор 1 раз вызывается на все замеры а не перед каждым замером, печально придется как-то обходить.


S>Понятно, что перформанс этой вставки зависит не от подробностей инициализации, а от посторонних вещей — типа пришлось ли сделать GC при перевыделении буфера во время именно этой вставки.



S>Я бы писал примерно так:

S>[cs]

S> [Benchmark]

S> public void InsertStringBuilderWithCapacity()
S> {
S> var stringBuilderWithCapacity = new StringBuilder(BaseStr.Length * RepeatCount + InsertCount);
S> stringBuilderWithCapacity.Append(GetString());
S> for(var i=0; i<InsertCount; i++)
S> stringBuilderWithCapacity.Insert(4, "5");
S> }


Да но в этом случае тест получается не совсем корректный — тут мы измеряем не только вставку но и создание экземпляра StringBuilder и копирование в него строки.
А хочется замерить только вставку в уже проинициализированный. Можно конечно создать дополнительные методы без вставки и сравнить время, но это как временный вариант — не уверен что это правильный способ.

p.s. попробовал сделать замер с методами инициализации и вставки получается тоже высокая погрешность и странности например метод InsertStringBuilder () меньше погрешность чем у метода InitStringBuilder (), хотя insert состоит из 2х частей такой же init и непосредственно операция вставки. Но есть и плюсы — метод Empty стал адекватнее измеряться.

|                          Method |      Job | InvocationCount | IterationCount | UnrollFactor | RepeatCount | BaseString |        Mean |         Error |      StdDev |      Median |
|-------------------------------- |--------- |---------------- |--------------- |------------- |------------ |----------- |------------:|--------------:|------------:|------------:|
|                       InsertStr | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 388.3000 ns |    80.3761 ns |  20.8734 ns | 398.5000 ns |
|                         InitStr | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 343.0000 ns |    94.7524 ns |  24.6069 ns | 342.0000 ns |
|             InsertStringBuilder | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 504.2000 ns |   246.6427 ns |  64.0523 ns | 497.0000 ns |
|               InitStringBuilder | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 691.1000 ns | 1,509.4735 ns | 392.0055 ns | 421.5000 ns |
| InsertStringBuilderWithCapacity | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 530.5000 ns |   322.6899 ns |  49.9366 ns | 516.5000 ns |
|   InitStringBuilderWithCapacity | QuickJob |             100 |              5 |            1 |          10 | 1234567890 | 536.0000 ns |   462.2056 ns | 120.0333 ns | 525.0000 ns |
|                           Empty | QuickJob |             100 |              5 |            1 |          10 | 1234567890 |   0.0000 ns |     0.0000 ns |   0.0000 ns |   0.0000 ns |
|                       InsertStr | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 277.5542 ns |   142.1498 ns |   7.7917 ns | 275.7582 ns |
|                         InitStr | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 240.7382 ns |   217.4022 ns |  11.9165 ns | 236.3251 ns |
|             InsertStringBuilder | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 354.2914 ns |   182.9307 ns |  10.0270 ns | 359.9009 ns |
|               InitStringBuilder | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 291.1609 ns |   253.5002 ns |  13.8952 ns | 285.7806 ns |
| InsertStringBuilderWithCapacity | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 339.9996 ns |    35.9658 ns |   1.9714 ns | 340.9990 ns |
|   InitStringBuilderWithCapacity | ShortRun |               1 |              3 |           16 |          10 | 1234567890 | 293.8455 ns |   112.6605 ns |   6.1753 ns | 291.8657 ns |
|                           Empty | ShortRun |               1 |              3 |           16 |          10 | 1234567890 |   0.0013 ns |     0.0216 ns |   0.0012 ns |   0.0015 ns |




настройки для быстрого тестирования взял из faq
https://benchmarkdotnet.org/articles/faq.html


  код бенчмарка
   [SimpleJob(launchCount: 1, warmupCount: 3, targetCount: 5, invocationCount: 100, id: "QuickJob")]
    [ShortRunJob]
    public class BenchmarkInsertCharTest
    {
            [Params(10)]
            public int RepeatCount { get; set; }
            [Params("1234567890")]
            public string BaseString { get; set; }

            [Benchmark]
            public void InsertStr()
            {
                var str = GetString();
                str = str.Insert(4, "5");
            }

            [Benchmark]
            public void InitStr()
            {
                var str = GetString();
            }

            [Benchmark]
            public void InsertStringBuilder()
            {
                var stringBuilder = new StringBuilder(GetString());
                stringBuilder.Insert(4, "5");
            }

            [Benchmark]
            public void InitStringBuilder()
            {
                var stringBuilder = new StringBuilder(GetString());
            }


            [Benchmark]
            public void InsertStringBuilderWithCapacity()
            {
                var stringBuilderWithCapacity = new StringBuilder(BaseString.Length * (RepeatCount + 1));
                stringBuilderWithCapacity.Append(GetString());
                stringBuilderWithCapacity.Insert(4, "5");
            }

            [Benchmark]
            public void InitStringBuilderWithCapacity()
            {
                var stringBuilderWithCapacity = new StringBuilder(BaseString.Length * (RepeatCount + 1));
                stringBuilderWithCapacity.Append(GetString());
            }


            [Benchmark]
            public void Empty()
            {
            }

            private string GetString() => string.Join(string.Empty, Enumerable.Repeat(BaseString, RepeatCount));
    }