Странное поведение секундомера - PullRequest
0 голосов
/ 03 ноября 2010
            Stopwatch sw = new Stopwatch();
            for (int i = 0; i < lines.Length; i++)
            {
                sw.Start();
                fn(); //call function
                sw.Stop();

            }
            Console.WriteLine(sw.ElapsedMilliseconds);


            long total =0;
            for (int i = 0; i < lines.Length; i++)
            {
                Stopwatch sw = Stopwatch.StartNew();
                fn(); //call function
                sw.Stop();
                total += sw.ElapsedMilliseconds;

            }
            Console.WriteLine(total);

Вывод не тот, у вас есть какое-либо объяснение для этого?

Ответы [ 4 ]

8 голосов
/ 03 ноября 2010

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

Предположим, что каждая итерация занимает 0,1 миллисекунды. Ваша общая сумма для второго цикла будет равна 0, потому что на каждой итерации она будет округлять истекшее время до 0 миллисекунд. Первый цикл отслеживает прошедшие тики .

Если оставить все это в стороне, вам все равно не следует запускать и останавливать таймер так часто - он будет портить ваши результаты. Вместо этого запустите секундомер один раз перед циклом и остановите его после цикла.

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

4 голосов
/ 03 ноября 2010

Потому что StartNew() и Stop() создают накладные расходы. По этой причине вы обычно проводите такие тесты с сотнями или тысячами итераций: чтобы минимизировать издержки производительности при измерениях реальной производительности.

1 голос
/ 03 ноября 2010

Накладные расходы цикла будут значительно меньше, чем накладные расходы на повторную остановку / запуск таймера и даже меньше в случае повторного создания нового.Таким образом, я бы запустил таймер перед циклом и завершил его после цикла и разделил бы ваше прошедшее время на количество итераций.Это даст вам гораздо более точные результаты.

1 голос
/ 03 ноября 2010

Вы, вероятно, сталкиваетесь с гранулярностью системного таймера. Иногда синхронизация тривиальной функции вернет 0 или 10 мс. Эта ошибка может добавляться в ваш тест.

Вероятно, вы увидите аналогичный результат, если дважды запустите первый цикл или второй цикл.

...