Environment.TickCount против DateTime.Now

Можно ли когда-нибудь использовать Environment.TickCount для расчета промежутков времени?

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");

Кроме того, если вы ДЕЙСТВИТЕЛЬНО используете DateTime для математических вычислений, связанных с датой, всегда используйте DateTime.UtcNow как DateTime.Now подвержен переходу на летнее время ... ваши расчеты могут отличаться на час или, что еще хуже, с отрицательными числами.

Scott Hanselman 03.07.2009 11:04

@Scott: Подумал, что стоит упомянуть: даже с UtcNow существует проблема запланированных синхронизаций NTP: не так уж редко системное время изменяется примерно на 10 секунд после этих обновлений (на моем ПК).

Groo 30.09.2011 16:13

Математика на самом деле не так уж и сложна ... int duration = unchecked((int)((uint)Environment.TickCount - (uint)start)); ... даст вам правильный ответ независимо от опрокидывания. (В идеале вы можете пропустить трансляцию, чтобы вернуться к int, если это вам абсолютно не нужно.)

AnorZaken 11.10.2015 05:18

@AnorZaken: вам действительно не нужен Любые этого кастинга, как объяснил в другом месте на этой странице.

Glenn Slayden 24.02.2017 01:07

@GlennSlayden Вы правы - мои приведения uint бессмысленны и, так как я возвращаюсь к int, это все равно не имеет никакого смысла. Хороший улов. (Кроме того, возврат к int внутри непроверенного контекста был сомнительным выбором с моей стороны.)

AnorZaken 24.02.2017 11:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
69
5
78 540
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Ответ принят как подходящий

Используйте класс секундомера. На 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;

Класс Stopwatch доступен только в .NET Framework 2.0 и выше. В более старых версиях вы использовали TickCount, возможно, в сочетании с TimeSpan, чтобы проверить, не пересекли ли вы 25-дневную границу.

Rune Grimstad 28.10.2008 17:06

В более ранних версиях вы бы использовали вызовы p / invoke, помеченные как безопасные и msdn.microsoft.com/en-us/library/ms901807.aspx, а не TickCount.

Henrik 18.02.2011 10:33

Если на оборудовании, на котором работает ваша программа, отсутствует таймер с высоким разрешением, секундомер так же опасен, как и DateTime. См. connect.microsoft.com/VisualStudio/feedback/details/741848/…, а также ответ Джоэла ниже.

rkagerer 14.01.2013 08:39

Мне не удалось найти секундомер для переносимой библиотеки классов, поэтому вам придется использовать обходной путь.

Alexey Strakh 20.08.2013 06:25

Вероятно, вам нужен System.Diagnostics.StopWatch.

Использовать

System.Diagnostics.Stopwatch

У него есть свойство, называемое

EllapsedMilliseconds

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

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

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

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

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

№3 действительно неверен, у Мэтьюза есть лучшее объяснение. Речь идет не об относительных временах, близких к циклу, это проблема только в том случае, если вы в мера раз больше, чем двадцать с чем-то дней.

SoftMemes 20.04.2010 17:22

# 3 даже совершенно неверно, это очень большая проблема, если вы запускаете что-либо, что может быть использовано в качестве службы на сервере, или даже приложения на сервере терминалов. Даже мой домашний компьютер иногда работает в течение двух или более месяцев без перезагрузки (не знаю, как режим ожидания и гибернация влияют на тики, но я подозреваю, что они не сбрасывают их).

Lucero 27.09.2010 15:59

@Lucero и Freed: вам определенно стоит нет использовать Stopwatch (или TickCount, если на то пошло) для измерения таких длинных интервалов времени. Несмотря на высокую степень детализации, Stopwatch имеет очень низкую долгосрочную точность, начиная с 5-10 секунд (не миллисекунды) в день.

MusiGenesis 27.09.2010 17:14

Меня просто сбило с толку, когда мой алгоритм завис. Этот компьютер работал / находился в спящем режиме> 24 дней. Как противно!

usr 10.01.2011 15:26

@usr: это твоя вина за использование TickCount для измерения такого длительного промежутка времени. :)

MusiGenesis 12.01.2011 19:55

Конечно было! Я хотел поделиться, чтобы другие не были укушены этим.

usr 15.01.2011 01:53

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

usr 10.05.2012 00:13

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

Для одноразового тайминга еще проще написать

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

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

Почему вас беспокоит опрокидывание? Пока измеряемая продолжительность составляет менее 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, позволяет процессору справиться с этим.

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

Лучший ответ ИМО. Он также не будет «взорваться через 50 дней», если только вы не попытаетесь использовать его для отслеживания периода времени, превышающего 50 дней, что кажется очень маловероятным. +1 за твердое доказательство того, что это работает легко проверяемым способом. StopWatch - бесполезный класс из-за проблем с многоядерными процессорами, которые в наши дни есть практически на каждом ПК.

eselk 17.10.2015 01:07

Примечание. Этот пример будет работать только в том случае, если код построен без проверки арифметического переполнения / потери значимости (по умолчанию она отключена). Если включить (Настройки проекта> Сборка> Дополнительно ...> Проверить арифметическое переполнение / потерю значимости) код поднимет System.OverflowException : Arithmetic operation resulted in an overflow

Lu55 13.03.2017 20:39

@eselk StopWatch в настоящее время надежен: см. stackoverflow.com/questions/243351/…

Lu55 13.03.2017 20:47

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

К сожалению, Stopwatch.GetTimestamp() не так удобен, как Environment.TickCount, потому что он возвращает некоторые абстрактные тики, а не обычные единицы времени.

Lu55 18.11.2015 01:36

Если есть соображения высокой производительности, следует отметить, что Environment.TickCount значительно быстрее. Тесты здесь (blog.tedd.no/2015/08/17/…) показывают следующее относительное время: Environment.TickCount: 7 мс, DateTime.Now.Ticks: 1392 мс, Stopwatch.ElapsedMilliseconds: 933 мс.

Special Sauce 01.04.2017 20:23

@SpecialSauce, но обратите внимание, что это не эквивалентные вещи. Также обратите внимание, что в моей системе Stopwatch.ElapsedMilliseconds примерно в 35 раз быстрее, чем его система, в то время как мой Environment.TickCount всего в ~ 2 раза быстрее. Ваше оборудование может (очевидно) отличаться.

Mark 03.04.2017 02:26

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

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

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

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

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

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

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

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();
}

Просто примечание для вашего цикла - вместо цикла for вы должны использовать Thread.SpinWait, это гарантирует, что цикл не будет оптимизирован (вы указываете, что цикл «отходов» - это то, что вы хотите семантически). В остальном он должен быть более или менее эквивалентен циклу for.

Luaan 14.03.2014 14:43

@Luaan: спасибо, я изменил пример кода, чтобы показать точность.

mistika 14.03.2014 15:26

Очень подробное объяснение. На моем рабочем столе win10 результаты аналогичны вашему отчету Win7. Секундомер выглядит очень точным. DateTime: 10278 мс, за 21272 цикла Среда: 16 мс, за 4786363 цикла Секундомер: 0,00087671156014329 мс, за 1 цикл

Patrick Stalph 10.11.2016 16:23

Небольшое исправление в вашем превосходном коде: поскольку Stopwatch полностью пассивен (в том смысле, что он никогда не запускает какой-либо код сам по себе), ваш вызов sw.Stop() бессмысленен, вызывая только шквал бесполезных вызовов Win32. Обычно этот вопрос не стоит упоминания, но здесь он привлекает внимание к тому, что некоторым может показаться нелогичным, а именно, что Stopwatch неактивен (также не IDisposable) и от него можно в любой момент отказаться. Оскорбленная «интуиция» состоит в том, что что-то «оставили запущенным», но понимание может упростить проектирование системы, связанной с (например) остановом, обычно сложной областью.

Glenn Slayden 24.02.2017 02:14

@GlennSlayden, вы правы. Я считаю, что это рудимент самой первой версии кода, который печатал sw.ElapsedTicks на stdout после sw.Stop ()

mistika 06.03.2017 22:40

QueryPerformanceCounter () в настоящее время надежен: см. blogs.msdn.microsoft.com/oldnewthing/20140822-01/?p=163 и msdn.microsoft.com/en-us/library/windows/desktop/…: «Q: Надежно ли работает QPC в многопроцессорных системах, многоядерных системах и системах с гиперпоточностью? A: Да», «Q: На точность QPC влияют изменения частоты процессора, вызванные управлением питанием или технологией Turbo Boost? A: Нет "

Lu55 13.03.2017 19:27

Отмеченные вами "проблемы" в наши дни по сути неактуальны. Они применимы к Windows 2000, Windows 2003 и Windows XP. Если вы используете эти системы, у вас будут большие проблемы, чем точность ваших таймеров.

Mark 03.04.2017 02:12

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 от меня, потому что это то, что я хотел узнать. Если кто-то читает это: почему в этом ответе нет комментариев и нет других голосов за / против ?? (по состоянию на 5/2017) Если что-то не так, прокомментируйте, если считаете, что это правильно, проголосуйте за.

Philm 03.05.2017 12:43

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

Philm 04.05.2017 01:29

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

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

"Is QPC accuracy affected by processor frequency changes caused by power management or Turbo Boost technology?
No. If the processor has an invariant TSC, the QPC is not affected by these sort of changes. If the processor doesn't have an invariant TSC, QPC will revert to a platform hardware timer that won't be affected by processor frequency changes or Turbo Boost technology.

Does QPC reliably work on multi-processor systems, multi-core system, and systems with hyper-threading?
Yes

How do I determine and validate that QPC works on my machine?
You don't need to perform such checks.

Which processors have non-invariant TSCs? [..Read further..] "

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

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

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

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

Measured: GetTickCount64() [ms]: 275
Measured: Environment.TickCount [ms]: 45
Measured: DateTime.UtcNow.Ticks [ms]: 167
Measured: Stopwatch: .ElapsedTicks [ms]: 277
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 548
Measured: static Stopwatch.GetTimestamp [ms]: 193
Measured: Stopwatch+conversion to DateTime [ms]: 551
Compare that with DateTime.Now.Ticks [ms]: 9010

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

Measured: GetTickCount64() [ms]: 198
Measured: Environment.TickCount [ms]: 39
Measured: DateTime.UtcNow.Ticks [ms]: 66 (!)
Measured: Stopwatch: .ElapsedTicks [ms]: 175
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 491
Measured: static Stopwatch.GetTimestamp [ms]: 175
Measured: Stopwatch+conversion to DateTime [ms]: 510
Compare that with DateTime.Now.Ticks [ms]: 8460

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

Measured: GetTickCount64() [ms]: 205
Measured: Environment.TickCount [ms]: 39
Measured: DateTime.UtcNow.Ticks [ms]: 127
Measured: Stopwatch: .ElapsedTicks [ms]: 209
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 285
Measured: static Stopwatch.GetTimestamp [ms]: 187
Measured: Stopwatch+conversion to DateTime [ms]: 319
Compare that with DateTime.Now.Ticks [ms]: 3040

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

Measured: GetTickCount64() [ms]: 148
Measured: Environment.TickCount [ms]: 31 (is it still worth it?)
Measured: DateTime.UtcNow.Ticks [ms]: 76 (!)
Measured: Stopwatch: .ElapsedTicks [ms]: 178
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 226
Measured: static Stopwatch.GetTimestamp [ms]: 175
Measured: Stopwatch+conversion to DateTime [ms]: 246
Compare that with DateTime.Now.Ticks [ms]: 3020

Может быть очень интересно, что создание значения DateTime для печати времени секундомера, похоже, почти не требует затрат. Интересно скорее с академической, чем с практической точки зрения, то, что статический секундомер работает немного быстрее (как и ожидалось). Некоторые моменты оптимизации весьма интересны. Например, я не могу объяснить, почему только 32-битный Stopwatch.ElapsedMilliseconds настолько медленный по сравнению с другими вариантами, например статическим. Это и 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();
    }

Компенсация переполнения

Как было сказано ранее, опрокидывание может произойти через 24,9 дня или, если вы используете безымянный слепок, через 49,8 дня. Поскольку я не хотел использовать pInvoke GetTickCount64, я написал эту компенсацию переполнения. В примере кода используется «байт», чтобы числа были под рукой. Пожалуйста, взгляните на него, он все еще может содержать ошибки:

using System;
namespace ConsoleApp1 {
    class Program {
        //
        static byte Lower = byte.MaxValue / 3;
        static byte Upper = 2 * byte.MaxValue / 3;
        //
        ///<summary>Compute delta between two TickCount values reliably, because TickCount might wrap after 49.8 days.</summary>
        static short Delta( byte next, byte ticks ) {
            if ( next < Lower ) {
                if ( ticks > Upper ) {
                    return (short) ( ticks - ( byte.MaxValue + 1 + next ) );
                }
            }
            if ( next > Upper ) {
                if ( ticks < Lower ) {
                    return (short) ( ( ticks + byte.MaxValue + 1 ) - next );
                }
            }
            return (short) ( ticks - next );
        }
        //
        static void Main( string[] args ) {
            // Init
            Random rnd = new Random();
            int max = 0;
            byte last = 0;
            byte wait = 3;
            byte next = (byte) ( last + wait );
            byte step = 0;
            // Loop tick
            for ( byte tick = 0; true; ) {
                //
                short delta = Delta( next, tick );
                if ( delta >= 0 ) {
                    Console.WriteLine( "RUN: last: {0} next: {1} tick: {2} delta: {3}", last, next, tick, delta );
                    last = tick;
                    next = (byte) ( last + wait );
                }
                // Will overflow to 0 automatically
                step = (byte) rnd.Next( 0, 11 );
                tick += step;
                max++; if ( max > 99999 ) break;
            }
        }
    }
}

Я не измерял и не сравнивал производительность этого алгоритма компенсации.

cskwg 13.07.2020 09:36

TickCount64

Проведя несколько быстрых измерений этой новой функции, я обнаружил (оптимизировано, выпуск 64-бит, циклы 1000mio):

Environment.TickCount: 2265
Environment.TickCount64: 2531
DateTime.UtcNow.Ticks: 69016

Измерения для неоптимизированного кода были аналогичными. Код теста:

static void Main( string[] args ) {
    long start, end, length = 1000 * 1000 * 1000;
    start = Environment.TickCount64;
    for ( int i = 0; i < length; i++ ) {
        var a = Environment.TickCount;
    }
    end = Environment.TickCount64;
    Console.WriteLine( "Environment.TickCount: {0}", end - start );
    start = Environment.TickCount64;
    for ( int i = 0; i < length; i++ ) {
        var a = Environment.TickCount64;
    }
    end = Environment.TickCount64;
    Console.WriteLine( "Environment.TickCount64: {0}", end - start );
    start = Environment.TickCount64;
    for ( int i = 0; i < length; i++ ) {
        var a = DateTime.UtcNow.Ticks;
    }
    end = Environment.TickCount64;
    Console.WriteLine( "DateTime.UtcNow.Ticks: {0}", end - start );
}

Другие вопросы по теме