Страницы

Поиск по вопросам

среда, 11 декабря 2019 г.

Как точно измерить время выполнения операции С#?

#c_sharp


Для лабораторной работы нужно измерить время выполнения нескольких операций.
Код:

StartTime = Environment.TickCount;
for (int i = 0; i < 2499; i++)
{
    LQL.Rem();
}
ResultTime = Environment.TickCount - StartTime;

public class LinkedStackLarge
{
    LinkedList _LinkedStack = new LinkedList();
    public LargeData Rem()
    {
        LargeData data = _LinkedStack.Last();
        _LinkedStack.RemoveFirst();
        return data;
    }
}


Код работает, но ResultTime в конце равно 0. Это нормально?
    


Ответы

Ответ 1



Измерение времени операции — на самом деле сложный момент, тут есть много тонкостей. Первая тонкость — JIT warm-up: когда метод исполняется в первый раз, происходит JIT-компиляция кода этого метода. Поэтому чтобы получить правильное время, необходимо перед измерением выполнить измеряемый код «вхолостую». Следующая тонкость: при запуске из-под отладчика JIT-компилятор даже в Release mode оптимизирует ваш код не очень агрессивно, чтобы в отладчике всё ещё были видны нужные переменные и стек вызовов. Запускайте ваши тесты из-под командной строки вне Visual Studio. Следующая тонкость: если ваш измеряемый метод не производит побочных эффектов и не возвращает значения, или возвращаемое значение игнорируется, то оптимизатор может выбросить его вызов. Поэтому обязательно выводите возвращаемое значение на экран. Следующая тонкость: метод может быть очень быстрым, и сумманое время выполнения может оказаться в пределах разрешения используемого вами таймера. Для того, чтобы реально измерить его скорость, нужно выполнить его N раз, и суммарное время поделить на N. Число N проще всего подобрать экспериментально. Следующая тонкость: различные таймеры имеют различное разрешение. Лучше брать более точный таймер. Я, например, использую Stopwatch, о котором говорит @Denis Bubnov в своём ответе. Следующая тонкость: выполнение может быть прервано различными внешними событиями. Например, системный планировщик потоков может отобрать квант времени у вашего потока, в результате измеренное значение будет больше, чем реальное время пробега метода. Или сборщик мусора может заморозить ваш поток. Поэтому имеет смысл проводить измерения несколько раз, и отбрасывать результаты, которые статистически слишком далеко отстоят от среднего значения. Следующая тонкость: различные системные кеши. Например, если ваш код читает файл, то после первого чтения файл окажется в кеше операционной системы, и последующие выполнения того же метода будут быстрее. Поэтому в таком случае каждая новая итерация должна читать новый файл. Практически, учитывать эти штуки в своём коде каждый раз лень. Поэтому имеет смысл воспользоваться фреймворком для бенчмарков. Например, можно использовать BenchmarkDotNet. В этом случае ваш код будет выглядеть так: class Program { static void Main(string[] args) { var summary = BenchmarkRunner.Run(); Console.ReadKey(); } } public class Tester { [Benchmark] public void Test() { for (int i = 0; i < 2499; i++) { LQL.Rem(); } } }

Ответ 2



Используйте класс Stopwatch. Он предоставляет набор методов и свойств, которые можно использовать для точного измерения затраченного времени. var startTime = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0; i < 2499; i++) { LQL.Rem(); } startTime.Stop(); var resultTime = startTime.Elapsed; // elapsedTime - строка, которая будет содержать значение затраченного времени string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:000}", resultTime.Hours, resultTime.Minutes, resultTime.Seconds, resultTime.Milliseconds); Свойство Stopwatch.Elapsed - получает общее затраченное время, измеренное текущим экземпляром. Полезные ссылки: C# Получить среднее время выполнения 10 операций Stopwatch vs. using System.DateTime.Now for timing events Is DateTime.Now the best way to measure a function's performance? Environment.TickCount vs DateTime.Now Примечание: Environment.TickCount основана на GetTickCount функции WinAPI. Разрешение функции GetTickCount ограничено разрешением системного таймера, который обычно находится в диапазоне от 10 миллисекунд до 16 миллисекунд. Функция дает результат в миллисекундах, но фактическая точность составляет не более 16 миллисекунд. Таким образом, для коротких интервалов времени менее 16 миллисекунд вы получите 0. В этом ответе хорошо и понятно написано про Environment.TickCount, DateTime.Ticks и Stopwatch (правда на английском языке): Environment.TickCount vs DateTime.Now

Комментариев нет:

Отправить комментарий