DateTime.Now лучший способ измерить производительность функции?


474

Мне нужно найти узкое место и нужно точно измерить время.

Является ли следующий фрагмент кода лучшим способом измерения производительности?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);

Кстати, если вы не ищете что-то быстрое и грязные счетчики производительности могут быть использованы.
Джонатан С Дикинсон

1
Если вам нужна большая точность, используйте Stopwatch.GetTimestamp, в противном случае ответ хороший.
dbasnett

@dbasnett Можете ли вы уточнить детали в ответе?
Дэвид Басараб

В приведенном выше примере измените начало и конец времени на long и назначьте им Stopwatch.GetTimestamp вместо DateTime.Now. Требуемое время (конец-начало) /Stopwatch.Frequency.
dbasnett

Ответы:


649

Нет, это не так. Используйте секундомерSystem.Diagnostics)

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Секундомер автоматически проверяет наличие высокоточных таймеров.

Стоит отметить, что DateTime.Nowчасто это происходит немного медленнее, чем DateTime.UtcNowиз-за работы с часовыми поясами, летним временем и т. Д.

DateTime.UtcNow обычно имеет разрешение 15 мс. Посмотрите сообщение в блоге Джона Чепмена о DateTime.Nowточности для отличного резюме.

Интересные мелочи: секундомер возвращается, DateTime.UtcNowесли ваше оборудование не поддерживает высокочастотный счетчик. Вы можете проверить, использует ли секундомер аппаратное обеспечение для достижения высокой точности, посмотрев на статическое поле Stopwatch.IsHighResolution .


3
Я бы поместил один PerformWork (); перед секундомером для «разогрева».
DiVan

2
Также необходимо добавить рекомендацию о том, что если у вас PerformWork()очень мало, вы можете звонить ему несколько раз и вычислять среднее значение для пакета вызовов. Кроме того, измеряйте целую серию вызовов вместо того, чтобы начинать / останавливать свой, Stopwatchчтобы избежать строб-эффекта, который испортит ваши измерения времени.
devgeezer

1
Секундомер не является многопоточным. См. Stackoverflow.com/questions/6664538/… и stackoverflow.com/questions/1149485/…
Павел Савара

1
sw.ElapsedMilliseconds; может также
Flappy

2
@Pavel, для ясности, Microsoft рекомендует секундомер как лучшее решение (с низкими издержками и высокой точностью) на современных многоядерных процессорах, работающих под управлением Windows 7 и Windows 8. msdn.microsoft.com/en-us/library/windows/ рабочий стол /…
Рон

91

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

Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();

Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);

В качестве альтернативы, если вам нужно что-то более сложное, вы, вероятно, должны рассмотреть возможность использования стороннего профилировщика, такого как ANTS .


56

В этой статье говорится, что в первую очередь вам нужно сравнить три альтернативы Stopwatch, DateTime.NowAND DateTime.UtcNow.

Это также показывает, что в некоторых случаях (когда счетчик производительности не существует) Секундомер использует DateTime.UtcNow + некоторая дополнительная обработка. Из-за этого очевидно, что в этом случае DateTime.UtcNow является лучшим вариантом (потому что другие используют его + некоторая обработка)

Однако, как оказалось, счетчик почти всегда существует - см. Объяснение о счетчике производительности с высоким разрешением и его существовании, связанном с .NET Stopwatch? ,

Вот график производительности. Обратите внимание, как низкая стоимость производительности UtcNow по сравнению с альтернативами:

Введите описание изображения здесь

Ось X - это размер выборки, а ось Y - относительное время примера.

Одна вещь , Stopwatchлучше в том , что она обеспечивает более высокие измерения времени разрешения. Другой - это его более оо-природа. Тем не менее, создание OO-оболочки UtcNowне может быть трудным.


Первая ссылка не работает.
Питер Мортенсен,

1
сломался да .. машина времени может показать это я бы догадался. Кстати, почему вы редактируете «тройку», я считаю, что в этом нет необходимости.
Валентин Кузуб,

18

Полезно поместить ваш код тестирования в служебный класс / метод. StopWatchКласс не должен быть Disposedили Stoppedв случае ошибки. Так, простейший код времени некоторые действия является

public partial class With
{
    public static long Benchmark(Action action)
    {
        var stopwatch = Stopwatch.StartNew();
        action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }
}

Образец кода вызова

public void Execute(Action action)
{
    var time = With.Benchmark(action);
    log.DebugFormat(“Did action in {0} ms.”, time);
}

Вот версия метода расширения

public static class Extensions
{
    public static long Benchmark(this Action action)
    {
        return With.Benchmark(action);
    }
}

И пример кода вызова

public void Execute(Action action)
{
    var time = action.Benchmark()
    log.DebugFormat(“Did action in {0} ms.”, time);
}

1
Как насчет лучшей детализации? Многие вещи происходят менее чем за одну мс.
Хенрик

Затем верните свойство Elapsed, это TimeSpan. Я просто показываю вам шаблон. Удачи в реализации этого.
Энтони Мастреан

Вернитесь Elapsed.TotalMillisecondsдля более высокой точности. Смотрите этот вопрос тоже stackoverflow.com/questions/8894425/…
nawfal

16

Функциональность секундомера была бы лучше (более высокая точность). Я также рекомендовал бы просто загрузить один из популярных профилировщиков (тем не менее , я использовал DotTrace и ANTS ... бесплатная пробная версия для DotTrace полностью функциональна и не раздражает, как некоторые другие).


13

Используйте класс System.Diagnostics.Stopwatch.

Stopwatch sw = new Stopwatch();
sw.Start();

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.

11

То же самое Секундомер, это намного лучше.

Что касается измерения производительности, вы должны также проверить, является ли ваш "// Some Execution Process" очень коротким процессом.

Также имейте в виду, что первый запуск "// Some Execution Process" может быть намного медленнее, чем последующие.

Обычно я тестирую метод, выполняя его 1000 или 1000000 циклов, и получаю гораздо более точные данные, чем один раз.


9

Все это отличные способы измерения времени, но это только очень косвенный способ найти узкие места.

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

Вот более полное объяснение того, как и почему это работает


7

@ Шон Чемберс

К вашему сведению, класс .NET Timer не предназначен для диагностики, он генерирует события с заданным интервалом, например так (из MSDN ):

System.Timers.Timer aTimer;
public static void Main()
{
    // Create a timer with a ten second interval.
    aTimer = new System.Timers.Timer(10000);

    // Hook up the Elapsed event for the timer.
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

    // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer.Interval = 2000;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program.");
    Console.ReadLine();
}

// Specify what you want to happen when the Elapsed event is 
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}

Так что это действительно не поможет вам узнать, сколько времени заняло что-то, просто прошло определенное количество времени.

Таймер также отображается в качестве элемента управления в System.Windows.Forms ... его можно найти в наборе инструментов вашего дизайнера в VS05 / VS08


6

Это правильный путь:

using System;
using System.Diagnostics;

class Program
{
    public static void Main()
    {
        Stopwatch stopWatch = Stopwatch.StartNew();

            // some other code

        stopWatch.Stop();

        // this not correct to get full timer resolution
        Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);

        // Correct way to get accurate high precision timing
        Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
    }
}

Для получения дополнительной информации перейдите к использованию секундомера вместо DataTime для получения точного счетчика производительности .


6

Visual Studio Team System имеет некоторые функции, которые могут помочь с этой проблемой. По сути, вы можете писать модульные тесты и смешивать их в различных сценариях, чтобы запускать их против вашего программного обеспечения в рамках стресс-теста или нагрузочного теста. Это может помочь определить области кода, которые больше всего влияют на производительность ваших приложений.

У группы Microsoft Patterns and Practices есть некоторые рекомендации в Руководстве по тестированию производительности системы Visual Studio Team .


5

Я только что нашел пост в блоге Вэнса Моррисона о классе CodeTimer, который он написал, который облегчает использование StopWatchи делает некоторые полезные вещи на стороне.


5

Это не достаточно профессионально:

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Более надежная версия:

PerformWork();

int repeat = 1000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
   PerformWork();
}

sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat);

В своем реальном коде я добавлю вызов GC.Collect для изменения управляемой кучи в известное состояние и добавлю вызов режима ожидания, чтобы различные интервалы кода можно было легко разделить в профиле ETW.


4

Я очень мало проверил такого рода проверку производительности (я склонен думать, что «это медленно, сделайте это быстрее»), поэтому я почти всегда с этим справлялся.

Google показывает много ресурсов / статей для проверки производительности.

Многие упоминают об использовании pinvoke для получения информации о производительности. Множество материалов, которые я изучаю, на самом деле упоминают только использование perfmon.

Редактировать:

Видел разговоры о StopWatch .. Здорово! Я кое-чему научился :)

Это похоже на хорошую статью


4

В моих программах я использую класс StopWatch, как показано здесь.

Stopwatch sw = new Stopwatch();
sw.Start();


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.