dotnetBenchmark как готовить
От: okon  
Дата: 07.07.20 13:56
Оценка:
Хотелось померять скорость вставки строки в разных сценариях с обычной строкой, билдер без выделенного заранее буффера и билдер с буффером.
Если не настраивать параметры теста, то по умолчанию он начинает длинные танцы с бубном, я так и не дождался результата за 5 минут и не понятно когда ждать завершения — прервал.

Если настраивать на минимализм, то выдает результат быстро но не нравится погрешность результата
и немного странно что пустой метод работает медленнее чем вставка ( на самом деле высокая погрешность ), погрешность у вставки могу понять — требуется выделение памяти и оно сильно зависит от разных условий.
Но вот почему пустой метод имеет такую высокую погрешность не понятно.




|                          Method |     Mean |     Error |    StdDev |    Median |       Min |      Max |
|-------------------------------- |---------:|----------:|----------:|----------:|----------:|---------:|
|                       InsertStr | 41.84 us | 183.25 us | 121.21 us | 3.6000 us | 2.9000 us | 386.8 us |
|             InsertStringBuilder | 95.50 us | 446.81 us | 295.54 us | 1.8500 us | 0.7000 us | 936.6 us |
| InsertStringBuilderWithCapacity | 41.91 us | 192.41 us | 127.27 us | 1.4500 us | 0.7000 us | 404.1 us |
|                           Empty | 81.96 us | 390.78 us | 258.48 us | 0.2000 us | 0.1000 us | 817.6 us |



Вопрос — какие настройки оптимальны по точности и скорости выполнения теста ( хотелось чтобы тест выполнялся не более минуты )


    [SimpleJob(RunStrategy.ColdStart, launchCount: 1, warmupCount: 0, targetCount: 10)]
    [MinColumn, MaxColumn, MeanColumn, MedianColumn]
    public class BenchmarkTest
    {

        string str;
        StringBuilder stringBuilder;
        StringBuilder stringBuilderWithCapacity;

        const string baseStr = "1234567890";
        const int repeatCount = 10;

        public BenchmarkTest()
        {
            str = string.Join(string.Empty, Enumerable.Repeat(baseStr, repeatCount));
            stringBuilder = new StringBuilder(str);
            stringBuilderWithCapacity = new StringBuilder(baseStr.Length * (repeatCount + 1));
            stringBuilderWithCapacity.Append(str);
        }

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

        [Benchmark]
        public void InsertStringBuilder()
        {
            stringBuilder.Insert(4, "5");
        }

        [Benchmark]
        public void InsertStringBuilderWithCapacity()
        {
            stringBuilderWithCapacity.Insert(4, "5");
        }


        [Benchmark]
        public void Empty()
        {
        }
    }
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Re: dotnetBenchmark как готовить
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.07.20 15:34
Оценка:
Здравствуйте, okon, Вы писали:

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

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

Не очень понятно, что именно вы хотите измерить.
BDN предполагает, что замеряемая операция воспроизводима.

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

То, что у вас написано — это стрингбилдер инициализируется 1 раз, затем в него охулиард раз вставляется 1 символ.
Понятно, что перформанс этой вставки зависит не от подробностей инициализации, а от посторонних вещей — типа пришлось ли сделать GC при перевыделении буфера во время именно этой вставки.
Я бы писал примерно так:
public class BenchmarkTest
{
  [Param(1000)]
  public int InsertCount{get;set;}
  [Param(10)]
  public int RepeatCount{get;set;}
  [Param("1234567890")]
  public string BaseString{get;set;}

  [Benchmark]
  public void InsertStr()
  {
    var str = GetString();
    for(var i = 0; i<InsertCount; i++)
      str = str.Insert(4, "5");
  }

  [Benchmark]
  public void InsertStringBuilder()
  {
    stringBuilder = new StringBuilder(GetString());
    for(var i=0; i<InsertCount; i++)
      stringBuilder.Insert(4, "5");
  }

  [Benchmark]
  public void InsertStringBuilderWithCapacity()
  {
    var stringBuilderWithCapacity = new StringBuilder(BaseStr.Length * RepeatCount + InsertCount);
    stringBuilderWithCapacity.Append(GetString());
    for(var i=0; i<InsertCount; i++)
      stringBuilderWithCapacity.Insert(4, "5");
  }
  [Benchmark]
  public void Empty()
  {
  }
  private string GetString()=> string.Join(string.Empty, Enumerable.Repeat(BaseStr, RepeatCount));
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: dotnetBenchmark как готовить
От: okon  
Дата: 07.07.20 17:41
Оценка:
Здравствуйте, 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));
    }


p.p.s
если убрать все аттрибуты настроек и запустить — то работате долго но результат похож на адекватный

|                          Method | RepeatCount | BaseString |        Mean |      Error |      StdDev |      Median |
|-------------------------------- |------------ |----------- |------------:|-----------:|------------:|------------:|
|                       InsertStr |          10 | 1234567890 | 342.5214 ns | 36.0737 ns | 106.3640 ns | 274.6993 ns |
|                         InitStr |          10 | 1234567890 | 231.9414 ns |  4.5347 ns |   5.2221 ns | 232.3379 ns |
|             InsertStringBuilder |          10 | 1234567890 | 337.4674 ns |  5.4300 ns |   5.0792 ns | 334.3936 ns |
|               InitStringBuilder |          10 | 1234567890 | 273.3300 ns |  5.4088 ns |   5.0594 ns | 271.9834 ns |
| InsertStringBuilderWithCapacity |          10 | 1234567890 | 346.3952 ns |  6.3896 ns |   5.9769 ns | 345.3614 ns |
|   InitStringBuilderWithCapacity |          10 | 1234567890 | 290.4882 ns |  5.8157 ns |   9.3912 ns | 290.0036 ns |
|                           Empty |          10 | 1234567890 |   0.0390 ns |  0.0196 ns |   0.0163 ns |   0.0399 ns |


т.е. получаем операции
string.Insert = 110 +- 40 us
StringBuilderInsert = 70+-10 us
StringBuilderInsert(WithCapacity) = 50+-10us

Что впринципе логично и объяснимо.
У нас это называлась подгонка результата ))
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Отредактировано 07.07.2020 20:05 okon . Предыдущая версия . Еще …
Отредактировано 07.07.2020 19:56 okon . Предыдущая версия .
Отредактировано 07.07.2020 19:53 okon . Предыдущая версия .
Отредактировано 07.07.2020 19:27 okon . Предыдущая версия .
Re: dotnetBenchmark как готовить
От: StatujaLeha на правах ИМХО
Дата: 07.07.20 20:14
Оценка: 2 (1)
Здравствуйте, okon, Вы писали:

Оно?
    class Program
    {
        static void Main(String[] args)
        {
            BenchmarkSwitcher.FromTypes(
                new []
                    {
                        typeof(InsertFullStringBuilderBenchmark), typeof(InsertNotFullStringBuilderBenchmark)
                    }).RunAllJoined();
        }
    }

    [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.NetCoreApp31)]
    public class InsertFullStringBuilderBenchmark
    {
        public static readonly string BaseString = new string('x', 100);

        private StringBuilder stringBuilder;

        [IterationSetup]
        public void SetUp()
        {
            this.stringBuilder = new StringBuilder(BaseString);
        }

        [Benchmark]
        public void Insert()
        {
            this.stringBuilder.Insert(4, "y");
        }
    }

    [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.NetCoreApp31)]
    public class InsertNotFullStringBuilderBenchmark
    {
        public static readonly string BaseString = new string('x', 100);

        private StringBuilder stringBuilder;

        [IterationSetup]
        public void SetUp()
        {
            this.stringBuilder = new StringBuilder(BaseString, 1024);
        }

        [Benchmark]
        public void Insert()
        {
            this.stringBuilder.Insert(4, "y");
        }
    }


BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.900 (1909/November2018Update/19H2)
Intel Core i7-10710U CPU 1.10GHz, 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=3.1.201
  [Host]     : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), X64 RyuJIT
  Job-ICXOAS : .NET Core 3.1.3 (CoreCLR 4.700.20.11803, CoreFX 4.700.20.12001), X64 RyuJIT

Runtime=.NET Core 3.1  InvocationCount=1  RunStrategy=ColdStart  
UnrollFactor=1  

|                                Type | Method |     Mean |     Error |    StdDev |   Median |
|------------------------------------ |------- |---------:|----------:|----------:|---------:|
|    InsertFullStringBuilderBenchmark | Insert | 6.761 us | 14.576 us | 42.979 us | 1.600 us |
| InsertNotFullStringBuilderBenchmark | Insert | 6.227 us | 12.863 us | 37.926 us | 2.300 us |
Re[3]: dotnetBenchmark как готовить
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.07.20 02:19
Оценка: 4 (1)
Здравствуйте, okon, Вы писали:

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

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

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

Слишком большой разброс получится.

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


O>Во как, я просто привык что в тестах обычно на каждый вызов метод создается новый экземпляр.

Микробенчмарк так не сделаешь — время вызова обычно пренебрежимо мало по сравнению со временем создания экземпляра.
O>Тут видимо иначе, в документации я нашел пример с конструктором, но там не уточняется как этот экземпляр используется.
O>p.s. проверил экспериментально — да конструктор 1 раз вызывается на все замеры а не перед каждым замером, печально придется как-то обходить.
Надо читать https://benchmarkdotnet.org/articles/features/setup-and-cleanup.html

O>Да но в этом случае тест получается не совсем корректный — тут мы измеряем не только вставку но и создание экземпляра StringBuilder и копирование в него строки.

O>А хочется замерить только вставку в уже проинициализированный. Можно конечно создать дополнительные методы без вставки и сравнить время, но это как временный вариант — не уверен что это правильный способ.
Правильного способа нет. Можно использовать IterationSetup, но авторы не рекомендуют пользоваться им, если измеряемый метод работает меньше 100ms.

O>Что впринципе логично и объяснимо.

O>У нас это называлась подгонка результата ))
Вы выбрали сложный эксперимент. Вся закавыка — в том, что у измеряемой операции нет какого-то объективного времени выполнения.
Стоимость вставки одного символа зависит от слишком многих параметров — от длины строки, от позиции вставки, от состояния хипа в момент вставки, и т.д.
Независимо от того, какой методикой делать бенчмарк, подготовка воспроизводимости стоит слишком много по сравнению с самой операцией.
Поэтому придётся либо смириться с никакой точностью, либо подменить задачу на более удобную — например, замерить стоимость вставки не одного символа, а десятка тысяч по-символьных вставок.
Тогда можно говорить об "амортизированной стоимости" одной операции, поделив общее время на 10000.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: dotnetBenchmark как готовить
От: okon  
Дата: 08.07.20 07:31
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

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


SL>Оно?


SL> [IterationSetup]

SL> public void SetUp()
SL> {
SL> this.stringBuilder = new StringBuilder(BaseString, 1024);
SL> }

SL>| Type | Method | Mean | Error | StdDev | Median |

SL>|------------------------------------ |------- |---------:|----------:|----------:|---------:|
SL>| InsertFullStringBuilderBenchmark | Insert | 6.761 us | 14.576 us | 42.979 us | 1.600 us |
SL>| InsertNotFullStringBuilderBenchmark | Insert | 6.227 us | 12.863 us | 37.926 us | 2.300 us |

SL>[/cs]


Да структура кода похожа на то что хотелось бы, но вот в паралельной ветке ответили что InterationSetup не рекомендуется к использованию.
И вот результаты измерений не хорошие получаются — погрешность в 2 раза выше чем само значение.
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Re[4]: dotnetBenchmark как готовить
От: okon  
Дата: 08.07.20 07:40
Оценка:
Здравствуйте, Sinclair, Вы писали:


O>>Что впринципе логично и объяснимо.

O>>У нас это называлась подгонка результата ))
S> Вы выбрали сложный эксперимент. Вся закавыка — в том, что у измеряемой операции нет какого-то объективного времени выполнения.
S>Стоимость вставки одного символа зависит от слишком многих параметров — от длины строки, от позиции вставки, от состояния хипа в момент вставки, и т.д.
Согласен.

S>Независимо от того, какой методикой делать бенчмарк, подготовка воспроизводимости стоит слишком много по сравнению с самой операцией.

S>Поэтому придётся либо смириться с никакой точностью, либо подменить задачу на более удобную — например, замерить стоимость вставки не одного символа, а десятка тысяч по-символьных вставок.
S>Тогда можно говорить об "амортизированной стоимости" одной операции, поделив общее время на 10000.

Вот честно говоря я думал что бенчмарк так и работает — т.е. выполняет метод много раз и получает некое амортизированное значение
т.е. представим что в бенчмарке есть цикл

включаем секундомер
от 1 до N
  { 
   меряй меня
  }
выключаем секундомер


а я вставлю внутри еще один цикл

...
от 1 до N
   {
   от 1 до M
      меряй меня
   }
....


то это будет эквивалентно если в бенчмарке задать от 1 до N * M

если же там
от 1 до N
  включаем секундомер
     { 
        меряй меня
     }
  ставим на паузу секундомер

то тогда да если метод очень короткий — на грани точности секундомера то ничего не померить будет — но не понятно зачем так делать.
Лучше было бы замерить отдельно пустой цикл и потом вычесть разницу.
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Re[5]: dotnetBenchmark как готовить
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.07.20 13:27
Оценка: 2 (1)
Здравствуйте, okon, Вы писали:

O>Согласен.


O>Вот честно говоря я думал что бенчмарк так и работает — т.е. выполняет метод много раз и получает некое амортизированное значение

Не совсем так. Он выполняет метод много раз, но количество раз он подбирает так, чтобы получить заданную точность.
Если у нас повторные вызовы метода занимают всё больше и больше времени, то BDN будет растерян, т.к. измеряемая величина не сходится ни к чему конкретному.
O>т.е. представим что в бенчмарке есть цикл
O>
O>включаем секундомер
O>от 1 до N
O>  { 
O>   меряй меня
O>  }
O>выключаем секундомер
O>


O>а я вставлю внутри еще один цикл


O>
O>...
O>от 1 до N
O>   {
O>   от 1 до M
O>      меряй меня
O>   }
O>....
O>

O>то это будет эквивалентно если в бенчмарке задать от 1 до N * M
Нет — N в этом случае величина плавающая; BDN пытается её угадать методом подбора.
А проблема — в том, что "меряй меня" выполняется в заведомо различных стартовых условиях.
O>если же там
O>
O>от 1 до N
O>  включаем секундомер
O>     { 
O>        меряй меня
O>     }
O>  ставим на паузу секундомер
O>

O>то тогда да если метод очень короткий — на грани точности секундомера то ничего не померить будет — но не понятно зачем так делать.
O>Лучше было бы замерить отдельно пустой цикл и потом вычесть разницу.
Так и делается.
Нормальный способ для вашего случая — именно такой:
от 1 до N
  сбрасываем настройки
  включаем секундомер
     { 
        от 1 до M
          меряй меня
     }
  ставим на паузу секундомер

Тогда каждая итерация будет делать одинаковую работу (ну, плюс-минус). При работе с ВDN лучше не делать никаких предположений об N.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: dotnetBenchmark как готовить
От: okon  
Дата: 08.07.20 15:30
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Так и делается.

S>Нормальный способ для вашего случая — именно такой:
S>
S>от 1 до N
S>  сбрасываем настройки
S>  включаем секундомер
S>     { 
S>        от 1 до M
S>          меряй меня
S>     }
S>  ставим на паузу секундомер
S>

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

Попробовал такой вариант, интересные получились результаты, погрешности ок — порядка 1-2% от значения,
одинаковые значения с capacity и без — тоже понятны, т.к. билдер с недостатком памяти сделать всего 1 раз перевыделение памяти — это будет не заметно.

но странно что вставка string.Insert получилась быстрее чем StringBuilder.Insert


|                          Method | IterationCount | RepeatCount | BaseString |            Mean |         Error |        StdDev |          Median |
|-------------------------------- |--------------- |------------ |----------- |----------------:|--------------:|--------------:|----------------:|
|                       InsertStr |           1000 |          10 | 1234567890 | 127,498.5368 ns | 1,583.7130 ns | 1,481.4062 ns | 127,162.4756 ns |
|                         InitStr |           1000 |          10 | 1234567890 |     515.8298 ns |     8.9971 ns |     8.4159 ns |     516.8230 ns |
|             InsertStringBuilder |           1000 |          10 | 1234567890 | 163,196.2695 ns | 1,681.9218 ns | 1,573.2707 ns | 163,156.1768 ns |
|               InitStringBuilder |           1000 |          10 | 1234567890 |     551.0845 ns |     5.3757 ns |     4.4890 ns |     550.5586 ns |
| InsertStringBuilderWithCapacity |           1000 |          10 | 1234567890 | 163,173.2590 ns | 3,197.7588 ns | 3,140.6267 ns | 162,158.4106 ns |
|   InitStringBuilderWithCapacity |           1000 |          10 | 1234567890 |   1,290.3868 ns |     7.3575 ns |     6.5223 ns |   1,289.6532 ns |
|                           Empty |           1000 |          10 | 1234567890 |       0.0089 ns |     0.0180 ns |     0.0185 ns |       0.0000 ns |


  код теста
    public class BenchmarkInsertCharTest
    {

            [Params(1000)]
            public int IterationCount { get; set; }

            [Params(10)]
            public int RepeatCount { get; set; }
            [Params("1234567890")]
            public string BaseString { get; set; }

            [Benchmark]
            public void InsertStr()
            {
                var str = GetString();
                for (int i = 0; i < IterationCount; i++)
                    str = str.Insert(4, "5");
            }

            [Benchmark]
            public void InitStr()
            {
                var str = GetString();
                for (int i = 0; i < IterationCount; i++)
                { }
            }

            [Benchmark]
            public void InsertStringBuilder()
            {
                var stringBuilder = new StringBuilder(GetString());
                for (int i = 0; i < IterationCount; i++)
                    stringBuilder.Insert(4, "5");
            }

            [Benchmark]
            public void InitStringBuilder()
            {
                var stringBuilder = new StringBuilder(GetString());
                for (int i = 0; i < IterationCount; i++)
                {
                }
            }


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

            [Benchmark]
            public void InitStringBuilderWithCapacity()
            {
                var stringBuilderWithCapacity = new StringBuilder(BaseString.Length * (IterationCount + 1));
                stringBuilderWithCapacity.Append(GetString());
                for (int i = 0; i < IterationCount; i++)
                {
                }
            }


            [Benchmark]
            public void Empty()
            {
            }
            private string GetString() => string.Join(string.Empty, Enumerable.Repeat(BaseString, RepeatCount));
    }
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
Отредактировано 08.07.2020 15:32 okon . Предыдущая версия .
Re[3]: dotnetBenchmark как готовить
От: StatujaLeha на правах ИМХО
Дата: 08.07.20 18:57
Оценка: 4 (1)
Здравствуйте, okon, Вы писали:

O>Да структура кода похожа на то что хотелось бы, но вот в паралельной ветке ответили что InterationSetup не рекомендуется к использованию.

O>И вот результаты измерений не хорошие получаются — погрешность в 2 раза выше чем само значение.

Да, доработал тест.

    [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.NetCoreApp31)]
    public class InsertFullStringBuilderBenchmark
    {
        public static readonly string BaseString = new string('x', 100);

        private StringBuilder[] stringBuilder;

        [IterationSetup]
        public void SetUp()
        {
            this.stringBuilder = Enumerable.Repeat(1, 1000000).Select(e => new StringBuilder(BaseString)).ToArray();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        [Benchmark]
        public void Insert()
        {
            for (int i = 0; i < this.stringBuilder.Length; ++i)
            {
                this.stringBuilder[i].Insert(4, "y");
            }
        }
    }

    [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.NetCoreApp31)]
    public class InsertNotFullStringBuilderBenchmark
    {
        public static readonly string BaseString = new string('x', 100);

        private StringBuilder[] stringBuilder;

        [IterationSetup]
        public void SetUp()
        {
            this.stringBuilder = Enumerable.Repeat(1, 1000000).Select(e => new StringBuilder(BaseString, 1024)).ToArray();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        [Benchmark]
        public void Insert()
        {
            for (int i = 0; i < this.stringBuilder.Length; ++i)
            {
                this.stringBuilder[i].Insert(4, "y");
            }
        }
    }


Результаты только странные...
Runtime=.NET Core 3.1  InvocationCount=1  RunStrategy=ColdStart  
UnrollFactor=1  

|                                Type | Method |     Mean |    Error |   StdDev |   Median |
|------------------------------------ |------- |---------:|---------:|---------:|---------:|
|    InsertFullStringBuilderBenchmark | Insert | 446.5 ms | 30.01 ms | 88.47 ms | 405.8 ms |
| InsertNotFullStringBuilderBenchmark | Insert | 732.8 ms | 14.56 ms | 41.07 ms | 725.2 ms |
Re[4]: dotnetBenchmark как готовить
От: okon  
Дата: 08.07.20 21:04
Оценка:
Здравствуйте, StatujaLeha, Вы писали:


SL>Да, доработал тест.


SL> private StringBuilder[] stringBuilder;




SL>Результаты только странные...

SL>
SL>Runtime=.NET Core 3.1  InvocationCount=1  RunStrategy=ColdStart  
SL>UnrollFactor=1  

SL>|                                Type | Method |     Mean |    Error |   StdDev |   Median |
SL>|------------------------------------ |------- |---------:|---------:|---------:|---------:|
SL>|    InsertFullStringBuilderBenchmark | Insert | 446.5 ms | 30.01 ms | 88.47 ms | 405.8 ms |
SL>| InsertNotFullStringBuilderBenchmark | Insert | 732.8 ms | 14.56 ms | 41.07 ms | 725.2 ms |
SL>


Да предварительно создать все объекты в массиве хорошая идея.
Почему без Capacity получается почти в 1.5 раза быстрее — тоже пока не понятно.
”Жить стало лучше... но противнее. Люди которые ставят точку после слова лучше становятся сторонниками Путина, наши же сторонники делают акцент на слове противнее ( ложь, воровство, лицемерие, вражда )." (с) Борис Немцов
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.