2008-10-28 5 views
55

ли когда-либо OK, чтобы использовать Environment.TickCount рассчитать промежутки времени?Environment.TickCount против DateTime.Now

int start = Environment.TickCount; 
// Do stuff 
int duration = Environment.TickCount - start; 
Console.WriteLine("That took " + duration " ms"); 

Поскольку TickCount подписан и опрокидывание через 25 дней (это занимает 50 дней, чтобы поразить все 32 бит, но вы должны отказаться от подписанных немного, если вы хотите сделать какой-либо смысл математики), кажется, как это слишком рискованно, чтобы быть полезным.

Я использую DateTime.Now вместо этого. Это лучший способ сделать это?

DateTime start = DateTime.Now; 
// Do stuff 
TimeSpan duration = DateTime.Now - start; 
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms"); 
+11

Как неродственного в сторону, если вы используете DateTime для Дата, связанные с расчетами по математике, всегда использовать DateTime. UtcNow as DateTime.Now восприимчив к летнему времени ... ваши расчеты могут быть отключены на час или, что еще хуже, отрицательные числа. – 2009-07-03 07:04:51

+5

@Scott: Думаю, стоит упомянуть: даже с помощью `UtcNow` существует проблема запланированных синхронизаций NTP: не так редко, системное время изменяется на 10 секунд после этих обновлений (на моем ПК). – Groo 2011-09-30 12:13:29

+0

Математика на самом деле не такая сложная ... `int duration = unchecked ((int) ((uint) start.tickCount - (uint));` ... даст вам правильный ответ независимо от опрокидывания. (Вы можете в идеале пропустить бросок до «int», если он вам не нужен.) – AnorZaken 2015-10-11 02:18:15

ответ

64

Использование класса секундомера. Существует достойный пример на MSDN: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

Stopwatch stopWatch = Stopwatch.StartNew(); 
    Thread.Sleep(10000); 
    stopWatch.Stop(); 
    // Get the elapsed time as a TimeSpan value. 
    TimeSpan ts = stopWatch.Elapsed; 
+1

Класс Stopwatch доступен только в .NET Framework 2.0 и выше. В более старых версиях вы использовали TickCount, возможно, в сочетании с TimeSpan, чтобы проверить, пересекла ли вы границу 25 дней. – 2008-10-28 14:06:04

+8

Просто любопытно, сколько людей все еще использует 1.1 BCL? Даже Mono поддерживает 3.5. – Dykam 2009-07-13 21:55:40

8

Использование

System.Diagnostics.Stopwatch 

Он имеет свойство

EllapsedMilliseconds 
0

Вы должны использовать Stopwatch класс вместо этого.

0

Я использую Environment.TickCount потому что:

  1. Секундомер класс не в Compact Framework.
  2. Секундомер использует тот же самый базовый механизм синхронизации как TickCount, так что результаты не будут более или менее точным.
  3. Проблема обертывания с TickCount вряд ли будет поражена (вам нужно будет оставить свой компьютер на 27 дней, а затем попытаться измерить время, которое только что произойдет, чтобы охватить момент обертывания) и даже если бы вы ударили по нему, результат был бы огромным отрицательным временным интервалом (так что это будет выделяться).

Это, как говорится, я бы также рекомендовал использовать секундомер, если он доступен для вас. Или вы можете занять около 1 минуты и написать класс, похожий на секундомер, который обертывает Environment.TickCount.

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

0

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

0

Для синхронизации один выстрел, это даже проще написать

Stopwatch stopWatch = Stopwatch.StartNew(); 
...dostuff... 
Debug.WriteLine(String.Format("It took {0} milliseconds", 
           stopWatch.EllapsedMilliseconds))); 

Я думаю, космически вряд оберточного в TickCount даже меньше беспокойства для StopWatch, учитывая, что поле ElapsedTicks длинный. На моей машине StopWatch имеет высокое разрешение - 2.4e9 тиков в секунду.Даже в этом случае потребовалось бы более 121 года, чтобы переполнить поле клещей. Конечно, я не знаю, что происходит под обложками, поэтому возьмите это с солью. Тем не менее, я замечаю, что в документации для StopWatch даже не упоминается проблема с оберткой, в то время как документ для TickCount.

21

Почему вы беспокоитесь о опрокидывании? Пока продолжительность, которую вы измеряете, составляет менее 24,9 дней, и вы вычисляете продолжительность , вы в порядке. Неважно, как долго система работает, если вы только заботитесь о своей части этого времени работы (в отличие от прямого выполнения сравнений меньше или больше, чем в начальной и конечной точках). То есть это:

int before_rollover = Int32.MaxValue - 5; 
int after_rollover = Int32.MinValue + 7; 
int duration = after_rollover - before_rollover; 
Console.WriteLine("before_rollover: " + before_rollover.ToString()); 
Console.WriteLine("after_rollover: " + after_rollover.ToString()); 
Console.WriteLine("duration: " + duration.ToString()); 

правильно печатает:

before_rollover: 2147483642 
after_rollover: -2147483641 
duration: 13 

Вам не придется беспокоиться о знаковом бите. C#, как и C, позволяет процессору справиться с этим.

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

9

Если вы ищете функциональность Environment.TickCount но без накладных расходов на создание новых Stopwatch объектов, вы можете использовать статический метод Stopwatch.GetTimestamp() (наряду с Stopwatch.Frequency) для расчета длинных пролетов времени. Поскольку GetTimestamp() возвращает long, он не будет переполняться очень и очень долго (более 100 000 лет, на машине, которую я использую, чтобы написать это). Он также намного точнее, чем Environment.TickCount, который имеет максимальное разрешение от 10 до 16 миллисекунд.

77

Environment.TickCount основан на GetTickCount() Функция WinAPI. Это в миллисекундах Но фактическая точность составляет около 15,6 мс. Таким образом, вы не можете измерить более короткие интервалы времени (или вы получите 0)

Примечание: Возвращаемое значение - Int32, поэтому этот счетчик перевертывает каждые ~ 49,7 дней. Вы не должны использовать его для измерения таких длинных интервалов.

ДатаTime.Ticks основывается на GetSystemTimeAsFileTime() WinAPI функция. Это в 100 с наносекундах (десятки микрокосмонов). Фактическая точность DateTime.Ticks зависит от системы. В XP прирост системных часов составляет около 15,6 мс, то же, что и в среде Environment.TickCount. В Windows 7 его точность составляет 1 мс (в то время как Environemnt.TickCount по-прежнему составляет 15,6 мс), однако, если используется схема энергосбережения (обычно на ноутбуках), она может снизиться и до 15,6 мс.

Секундомер основан на QueryPerformanceCounter() Функция WinAPI (но если счетчик производительности с высокой разрешающей способностью не поддерживается вашей системой, DateTime.Клещи используются)

Перед использованием Секундомера заметить две проблемы:

  • может быть ненадежными на многопроцессорных системах (см MS kb895980, kb896256)
  • может быть ненадежным, если частота процессора меняется (читайте this статьи)

Вы можете оценить точность вашей системы с простым тестом:

static void Main(string[] args) 
{ 
    int xcnt = 0; 
    long xdelta, xstart; 
    xstart = DateTime.UtcNow.Ticks; 
    do { 
     xdelta = DateTime.UtcNow.Ticks - xstart; 
     xcnt++; 
    } while (xdelta == 0); 

    Console.WriteLine("DateTime:\t{0} ms, in {1} cycles", xdelta/(10000.0), xcnt); 

    int ycnt = 0, ystart; 
    long ydelta; 
    ystart = Environment.TickCount; 
    do { 
     ydelta = Environment.TickCount - ystart; 
     ycnt++; 
    } while (ydelta == 0); 

    Console.WriteLine("Environment:\t{0} ms, in {1} cycles ", ydelta, ycnt); 


    Stopwatch sw = new Stopwatch(); 
    int zcnt = 0; 
    long zstart, zdelta; 

    sw.Start(); 
    zstart = sw.ElapsedTicks; // This minimizes the difference (opposed to just using 0) 
    do { 
     zdelta = sw.ElapsedTicks - zstart; 
     zcnt++; 
    } while (zdelta == 0); 
    sw.Stop(); 

    Console.WriteLine("StopWatch:\t{0} ms, in {1} cycles", (zdelta * 1000.0)/Stopwatch.Frequency, zcnt); 
    Console.ReadKey(); 
} 
2

Environment.TickCount, кажется, гораздо быстрее, чем другие решения:

Environment.TickCount 71 
DateTime.UtcNow.Ticks 213 
sw.ElapsedMilliseconds 1273 

Измерения были сгенерированы с помощью следующего кода:

static void Main(string[] args) { 
    const int max = 10000000; 
    // 
    // 
    for (int j = 0; j < 3; j++) { 
     var sw = new Stopwatch(); 
     sw.Start(); 
     for (int i = 0; i < max; i++) { 
      var a = Environment.TickCount; 
     } 
     sw.Stop(); 
     Console.WriteLine($"Environment.TickCount {sw.ElapsedMilliseconds}"); 
     // 
     // 
     sw = new Stopwatch(); 
     sw.Start(); 
     for (int i = 0; i < max; i++) { 
      var a = DateTime.UtcNow.Ticks; 
     } 
     sw.Stop(); 
     Console.WriteLine($"DateTime.UtcNow.Ticks {sw.ElapsedMilliseconds}"); 
     // 
     // 
     sw = new Stopwatch(); 
     sw.Start(); 
     for (int i = 0; i < max; i++) { 
      var a = sw.ElapsedMilliseconds; 
     } 
     sw.Stop(); 
     Console.WriteLine($"sw.ElapsedMilliseconds {sw.ElapsedMilliseconds}"); 
    } 
    Console.WriteLine("Done"); 
    Console.ReadKey(); 
} 
1

Вот вид обновленного & обновленного резюме какие могут быть самые полезные ответы & комментариев в этой теме + дополнительные контрольные отметки и варианты:

Первое, что f irst: Как отмечали другие в комментариях, в последние годы все изменилось, и с «современными» Windows (Win XP ++) и .NET, а в современном оборудовании нет или мало причин не использовать секундомер(). Для получения более подробной информации см. MSDN. Котировки:?

«Является ли точность QPC влияние изменения частоты процессора, вызванным управлением питанием или технологией Turbo Boost
No. Если процессора имеет инвариантную TSC, то QPC не влияют на этих сортах изменений. Если процессор не имеет инвариантного TSC, QPC вернется к аппаратным таймерам платформы, на которые не будут влиять изменения частоты процессора или технология Turbo Boost.

QPC надежно работает на многопроцессорных системах , многоядерная система и системы с гиперпотоком?
Да

Как определить и подтвердить, что QPC работает на моей машине?
Вам не нужно выполнять такие проверки.

Какие процессоры имеют неинвариантные TSC? [..Read дальше ..] "

Но если вам не нужна точность Секундомер() или, по крайней мере, хотят точно знать о производительности Секундомер (статический vs. основе экземпляров) и другие возможные варианты, продолжайте читать:

Я взял на себя эталон выше из cskwg и расширил код для большего количества вариантов. Я измерил несколько лет i7 4700 MQ и C# 7 с VS 2017 (чтобы быть более точный, скомпилированный с .NET 4.5.2, несмотря на бинарные литералы, это C# 6 (используется для этого: строковые литералы и «использование статического»). Особенно производительность секундомера() кажется улучшенной по сравнению с упомянутым эталоном.

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

32 бит, режима выпуска без оптимизации :

Измеренные: GetTickCount64() [мс]: 275
Измерено: Environment.TickCount [мс]: 45
Измеренные: DateTime.UtcNow.Ticks [мс]:
Измеренный: Секундомер: .ElapsedTicks [мс]: 277
Измеренные: Секундомер: .ElapsedMilliseconds [мс]: 548
Измеренные: статическое Stopwatch.GetTimestamp [мс]: 193
Измерены: Секундомер + преобразование в DateTime [мс ]: 551
Сравните с DateTime.Now.Ticks [мс]:

32 битных, режим выпуска, оптимизированная:

Измеренные: GetTickCount64() [мс]: 198
Измерено: Environment.TickCount [мс]: 39
Измеренные: DateTime.UtcNow.Ticks [мс]: 66
Измеренный (!): Секундомер: .ElapsedTicks [мс]: 175
Измеренные: секундомер: .ElapsedMilliseconds [мс]:
Измеренные: статические Stopwatch.GetTimestamp [мс]: 175
Измеренные: секундомер + преобразование в DateTime [мс]:
Com Parè, что с DateTime.Now.Ticks [мс]:

64 бит, режим выпуска без оптимизации:

Измеренные: GetTickCount64() [мс]: 205
Измеренный : Environment.TickCount [мс]: 39
Измеренные: DateTime.UtcNow.Ticks [мс]:
Измеренные: секундомер: .ElapsedTicks [мс]: 209
Измеренные: Секундомер: .ElapsedMilliseconds [мс]: 285
Измеренные: статическое Stopwatch.GetTimestamp [мс]: 187
Измерены: Секундомер + преобразование в DateTime [мс]: 319
Сравните с DateTime.Now.Ticks [мс]: 3040

64 бит, режим выпуска, оптимизирован:

Измерено: GetTickCount64() [мс]: 148
Измерено: окружающая среда.TickCount [мс]: 31 (он все еще стоит?)
Измеренные: DateTime.UtcNow.Ticks [мс]: 76
Измеренные (!): Секундомер: .ElapsedTicks [мс]: 178
Измеренные: секундомер: .ElapsedMilliseconds [мс]: 226
Измерено: статические Stopwatch.GetTimestamp [мс]: 175
Измерено: секундомер + преобразование в DateTime [мс]: 246
Сравните с DateTime.Now.Ticks [мс ]: 3020

Может быть очень интересно, что , создавая значение DateTime для печати времени секундомера, похоже, почти не стоит. Интересным в более академичном, чем практический образом, является то, что статический секундомер немного быстрее (как и ожидалось). Некоторые точки оптимизации весьма интересны. Например, я не могу объяснить, почему Stopwatch.ElapsedMilliseconds только с 32-битным настолько медленным, по сравнению с другими вариантами, например, статическим. Это и DateTime.Now более чем удваивают их скорость с 64 бит.

Вы можете видеть: Только для миллионов исполнений время Секундомера начинает иметь значение. Если это действительно так (но будьте осторожны с микро-оптимизацией слишком рано), может быть интересно, что с GetTickCount64(), но особенно с DateTime.UtcNow, у вас есть 64-разрядный (длинный) таймер с меньшей точностью, чем секундомер, но быстрее, так что вам не нужно возиться с 32-разрядной «уродливой» средой Environment.TickCount.

Как и ожидалось, DateTime.Now на сегодняшний день является самым медленным из всех.

Если вы запустите его, код получит также текущую точность секундомера и многое другое.

Вот полный тест-код:

using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Threading; 
using static System.Environment; 

[...]

[DllImport("kernel32.dll") ] 
    public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start 

    static void Main(string[] args) 
    { 
     const int max = 10_000_000; 
     const int n = 3; 
     Stopwatch sw; 

     // Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx 
     // But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading 
     Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core 
     Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; 
     Thread.CurrentThread.Priority = ThreadPriority.Highest; 
     Thread.Sleep(2); // warmup 

     Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}"); 
     for (int j = 0; j < n; j++) 
     { 
      sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < max; i++) 
      { 
       var tickCount = GetTickCount64(); 
      } 
      sw.Stop(); 
      Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}"); 
      // 
      // 
      sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < max; i++) 
      { 
       var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days 
      } 
      sw.Stop(); 
      Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}"); 
      // 
      // 
      sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < max; i++) 
      { 
       var a = DateTime.UtcNow.Ticks; 
      } 
      sw.Stop(); 
      Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}"); 
      // 
      // 
      sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < max; i++) 
      { 
       var a = sw.ElapsedMilliseconds; 
      } 
      sw.Stop(); 
      Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}"); 
      // 
      // 
      sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < max; i++) 
      { 
       var a = Stopwatch.GetTimestamp(); 
      } 
      sw.Stop(); 
      Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}"); 
      // 
      // 
      DateTime dt=DateTime.MinValue; // just init 
      sw = new Stopwatch(); 
      sw.Start(); 
      for (int i = 0; i < max; i++) 
      { 
       var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference 
      } 
      sw.Stop(); 
      //Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}"); 
      Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]: {sw.ElapsedMilliseconds}"); 

      Console.WriteLine(); 
     } 
     // 
     // 
     sw = new Stopwatch(); 
     var tickCounterStart = Environment.TickCount; 
     sw.Start(); 
     for (int i = 0; i < max/10; i++) 
     { 
      var a = DateTime.Now.Ticks; 
     } 
     sw.Stop(); 
     var tickCounter = Environment.TickCount - tickCounterStart; 
     Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}"); 

     Console.WriteLine($"{NewLine}General Stopwatch information:"); 
     if (Stopwatch.IsHighResolution) 
      Console.WriteLine("- Using high-resolution performance counter for Stopwatch class."); 
     else 
      Console.WriteLine("- Using high-resolution performance counter for Stopwatch class."); 

     double freq = (double)Stopwatch.Frequency; 
     double ticksPerMicroSec = freq/(1000d*1000d) ; // microsecond resolution: 1 million ticks per sec 
     Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}"); 
     Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)"); 

     DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L); // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan 
     Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}"); 
     // this conversion from seems not really accurate, it will be between 24-25 days. 
     Console.WriteLine($"{NewLine}Done."); 

     while (Console.KeyAvailable) 
      Console.ReadKey(false); 
     Console.ReadKey(); 
    }