Окружающая среда.TickCount vs DateTime.Сейчас



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



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


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



я использую . Это лучший способ сделать это?



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

12 ответов:

использовать класс Stopwatch. Есть достойный пример на 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;

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

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

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

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

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

  • он может быть ненадежным на многопроцессорных системах (см. 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();
}

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

вы, вероятно, хотите System.Diagnostics.StopWatch.

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

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

System.Diagnostics.Stopwatch

Он имеет свойство под названием

EllapsedMilliseconds

окружающая среда.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();
}

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

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

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

надежно ли QPC работает на многопроцессорных системах, многоядерных системах и системах с гиперпоточностью?
да

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

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

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

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

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

32 бит режиме без оптимизации:

измерено: GetTickCount64 () [ms]: 275
Измеряется: Окружающая Среда.TickCount [МС]: 45
Измеряется: Дата И Время.UtcNow.Клещи [ms]:167
Измеряется: Секундомер: .ElapsedTicks [ms]: 277
Измеряется: Секундомер: .ElapsedMilliseconds [ms]: 548
Измеряется: статический секундомер.GetTimestamp [ms]: 193
Измеряется: секундомер + преобразование в DateTime [МС]: 551
Сравните это с DateTime.Сейчас.Клещи [ms]:9010

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

измерено: GetTickCount64 () [ms]: 198
Измеряется: Окружающая Среда.TickCount [МС]: 39
Измеряется: Дата И Время.UtcNow.Клещи [ms]: 66 (!)
Измеряется: Секундомер: .ElapsedTicks [ms]: 175
Измеряется: Секундомер: .ElapsedMilliseconds [ms]:491
Измеряется: статический секундомер.GetTimestamp [ms]: 175
Измеряется: секундомер + преобразование в DateTime [МС]:510
Сравните это с DateTime.Сейчас.Клещи [ms]:8460

64 бит режиме без оптимизации:

Измерено: GetTickCount64() [ms]: 205
Измеряется: Окружающая Среда.TickCount [МС]: 39
Измеряется: Дата И Время.UtcNow.Клещи [ms]:127
Измеряется: Секундомер: .ElapsedTicks [ms]: 209
Измеряется: Секундомер: .ElapsedMilliseconds [ms]: 285
Измеряется: статический секундомер.GetTimestamp [ms]: 187
Измеряется: секундомер + преобразование в DateTime [МС]: 319
Сравните это с DateTime.Сейчас.Клещей [МС]: 3040

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

измерено: GetTickCount64 () [ms]: 148
Измеряется: Окружающая Среда.TickCount [МС]: 31 (это все еще стоит?)
Измеряется: Дата И Время.UtcNow.Клещей [МС]: 76 (!)
Измеряется: Секундомер: .ElapsedTicks [ms]: 178
Измеряется: Секундомер: .ElapsedMilliseconds [ms]: 226
Измеряется: статический секундомер.GetTimestamp [ms]: 175
Измеряется: секундомер + преобразование в DateTime [МС]: 246
Сравните это с DateTime.Сейчас.Клещей [МС]: 3020

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

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

как и ожидалось, дата и время.Сейчас, безусловно, самый медленный из всех.

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

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

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

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

Я использую среду.Галочка потому что:

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

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

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

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

для синхронизации с одним выстрелом еще проще написать

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

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

Comments

    Ничего не найдено.