Чи є DateTime.Now найкращим способом вимірювання продуктивності функції?


474

Мені потрібно знайти вузьке місце і потрібно максимально точно виміряти час.

Чи є наступний фрагмент коду найкращим способом вимірювання ефективності?

DateTime startTime = DateTime.Now;

// Some execution process

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

До речі, якщо ви не шукаєте чогось швидкого і брудного лічильника, можна скористатися.
Джонатан С Дікінсон

1
Якщо вам потрібна більша точність, використовуйте Stopwatch.GetTimestamp, інакше відповідь хороша.
dbasnett

@dbasnett Чи можете ви детальніше описуватись у відповіді?
Давид Басараб

У наведеному вище прикладі змініть початковий і кінцевий час на довгий і призначте їм 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через роботу, яку доводиться виконувати з часовими поясами, DST тощо .

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, щоб бути зрозумілим, секундомір рекомендується корпорацією Майкрософт як найкраще рішення (низька накладна та висока точність) для сучасних багатоядерних процесорів, що працюють під керуванням 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.Nowі DateTime.UtcNow.

Це також показує, що в деяких випадках (коли лічильника продуктивності не існує) секундомір використовує DateTime.UtcNow + деяку додаткову обробку. Через це очевидно, що в такому випадку DateTime.UtcNow - найкращий варіант (адже інші використовують його + деяка обробка)

Однак, як виявляється, лічильник майже завжди існує - див. Пояснення щодо лічильника продуктивності високої роздільної здатності та його існування, пов’язаного з секундоміром .NET? .

Ось графік ефективності. Зверніть увагу, наскільки низька вартість продуктивності 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 / ...
Навфал

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

Діто секундомір, це набагато краще.

Що стосується вимірювання продуктивності, ви також повинні перевірити, чи є ваш "// деякий процес виконання" дуже коротким процесом.

Також майте на увазі, що перший запуск вашого "// деякого процесу виконання" може бути набагато повільнішим, ніж наступні запуски.

Зазвичай я тестую метод, виконуючи його 1000 або 1000000 разів у циклі, і я отримую набагато більш точні дані, ніж запускаючи його один раз.


9

Це все чудові способи вимірювання часу, але це лише дуже непрямий спосіб знайти вузькі місця.

Найбільш прямий спосіб знайти вузьке місце в нитці - це змусити його працювати, і поки він робить все, що змушує вас зачекати, зупиніть його з паузою або ключем перерви. Зробіть це кілька разів. Якщо ваше вузьке місце займає X% часу, X% - це ймовірність того, що ви впізнаєте його в акті на кожному знімку.

Ось більш повне пояснення того, як і чому це працює


7

@ Шон Чемберс

FYI, .NET Timer class не призначений для діагностики, він генерує події з заданим інтервалом, як це (з 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 має деякі функції, які можуть допомогти у вирішенні цієї проблеми. По суті, ви можете писати одиничні тести і змішувати їх у різних сценаріях, щоб працювати проти вашого програмного забезпечення в рамках стресового або навантажувального тесту. Це може допомогти визначити області коду, які найбільше впливають на продуктивність ваших програм.

Група «Шаблони та практики Microsoft» має деякі вказівки в Посібнику з тестування продуктивності системи Visual Studio .


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 для отримання інформації про продуктивність. Дуже багато матеріалів, які я вивчаю, справді згадуються з використанням парфмону.

Редагувати:

Побачили переговори про StopWatch .. Приємно! Я чомусь навчився :)

Це виглядає як гарна стаття


4

Те, як я користуюся в своїх програмах, використовує клас секундомір, як показано тут.

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.