Вопрос

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

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

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

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

DateTime start = DateTime.Now;
// Do stuff
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms");
Это было полезно?

Решение

Используйте класс секундомера. На MSDN есть хороший пример: http://msdn.microsoft .com / EN-US / библиотека / 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 основан на ПолучитьТикКаунт() Функция WinAPI.Это в миллисекундах, но фактическая точность его составляет около 15,6 мс.Таким образом, вы не можете измерить более короткие интервалы времени (или вы получите 0)

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

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

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

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

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

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

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 & Lt; например, непосредственно после пролонгации. Я всегда выполнял вычитание, чтобы найти продолжительность, которая учитывает опрокидывание, а затем основывал бы любые другие вычисления на продолжительности.

Возможно, вы хотите System.Diagnostics.StopWatch .

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

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

System.Diagnostics.Stopwatch

У него есть свойство с именем

EllapsedMilliseconds

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

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

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

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

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

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

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

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

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

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

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

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

32 бит, режим Release, оптимизировано:

Измерено:GetTickCount64() [мс]:198
Измерено:Environment.TickCount [мс]:39
Измерено:DateTime.UtcNow.Ticks [мс]:66 (!)
Измерено:Секундомер:.ElapsedTicks [мс]:175
Измерено:Секундомер:.Прошедшие миллисекунды [мс]: 491
Измерено:статический Stopwatch.GetTimestamp [мс]:175
Измерено:Секундомер+преобразование в DateTime [мс]: 510
Сравните это с DateTime.Now.Ticks [мс]: 8460

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

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

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

Измерено:GetTickCount64() [мс]:148
Измерено:Environment.TickCount [мс]:31 (стоит ли оно того?)
Измерено:DateTime.UtcNow.Ticks [мс]:76 (!)
Измерено:Секундомер:.ElapsedTicks [мс]:178
Измерено:Секундомер:.Прошедшие миллисекунды [мс]:226
Измерено:статический Stopwatch.GetTimestamp [мс]:175
Измерено:Секундомер+преобразование в DateTime [мс]:246
Сравните это с DateTime.Now.Ticks [мс]:3020

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

Ты можешь видеть:Лишь для миллионов казней время Секундомера начинает иметь значение.Если это действительно так (но остерегайтесь слишком ранней микрооптимизации), может быть интересно, что с GetTickCount64(), но особенно с ДатаВремя.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();
    }

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

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

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

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

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

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top