Pergunta

É sempre OK para usar intervalos de tempo calcular Environment.TickCountto?

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

Porque TickCount é assinado e vai rolar depois de 25 dias (que leva 50 dias para atingir todos os 32 bits, mas você tem que descartar o bit assinado se você quiser fazer qualquer sentido da matemática), parece que é muito arriscado Ser útil.

Eu estou usando DateTime.Now vez. É esta a melhor maneira de fazer isso?

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

Solução

classe

Use Cronômetro. Há um exemplo decente no 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;

Outras dicas

Environment.TickCount é baseado em GetTickCount () WinAPI função. É em milissegundos Mas a precisão real do que é cerca de 15,6 ms. Então você não pode medir intervalos de tempo mais curtos (ou você vai ter 0)

Nota: O valor devolvido é Int32, então este contador rola sobre cada ~ 49,7 dias. Você não deve usá-lo para medir esses intervalos longos.

DateTime.Ticks é baseado em GetSystemTimeAsFileTime (WinAPI) função. É no 100s nanossegundos (décimos de microsoconds). A precisão real de DateTime.Ticks depende do sistema. No XP, o incremento do relógio do sistema é de cerca de 15,6 ms, o mesmo que em Environment.TickCount. No Windows 7 sua precisão é de 1 ms (enquanto Environemnt.TickCount de ainda é de 15,6 ms), no entanto, se um esquema de economia de energia é usado (geralmente em laptops) pode ir até 15,6 ms também.

Cronômetro é baseado em QueryPerformanceCounter () função WinAPI ( mas se contador de desempenho de alta resolução não é suportado pelo seu sistema, DateTime.Ticks é usado)

Antes de usar Aviso StopWatch dois problemas:

  • pode não ser confiável em sistemas com múltiplos processadores (ver MS kb895980 , kb896256 )
  • pode não ser confiável se a frequência da CPU varia (leia este artigo )

Você pode avaliar a precisão no seu sistema com teste simples:

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

Por que você está preocupado com rollover? Enquanto a duração você está medindo está sob 24.9 dias e você calcular o relação duração, você está bem. Não importa quanto tempo o sistema tem funcionado, desde que você só preocupação-se com a sua parte do que tempo de execução (em oposição a executar diretamente menos do que ou maior do que comparações no início e no fim). Ou seja, isto:

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

imprime corretamente:

 before_rollover: 2147483642
 after_rollover: -2147483641
 duration: 13

Você não precisa se preocupar com o bit de sinal. C #, como C, permite que a alça CPU isso.

Esta é uma situação comum eu correr em antes com contagens de tempo em sistemas embarcados. Eu nunca iria comparar beforerollover

Você provavelmente quer System.Diagnostics.StopWatch .

Se você está procurando a funcionalidade do Environment.TickCount mas sem a sobrecarga de criar novos objetos Stopwatch, você pode usar o método Stopwatch.GetTimestamp() estática (junto com Stopwatch.Frequency) para calcular intervalos de tempo longos. Porque GetTimestamp() retorna um long, não vai transbordar por muito, muito tempo (mais de 100 mil anos, na máquina que estou usando para escrever este). É também muito mais preciso do que Environment.TickCount que tem uma resolução máxima de 10 a 16 milissegundos.

Use

System.Diagnostics.Stopwatch

Ele tem uma propriedade chamada

EllapsedMilliseconds

Environment.TickCount parece ser muito mais rápido, então as outras soluções:

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

As medições foram gerados pelo código seguinte:

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

Aqui é uma espécie de resumo atualizado e revigorado do que pode ser o mais respostas úteis e comentários neste segmento + benchmarks extras e variantes:

Primeiro de tudo: Como outros têm apontado nos comentários, as coisas mudaram nos últimos anos e com o "moderno" Windows (Win XP ++) e .NET, e hardware moderno não existam ou pequenas razões para não usar Cronômetro (). Veja MSDN para mais detalhes. Citações:

"A precisão é QPC afetada pela frequência do processador alterações causadas pelo gerenciamento de energia ou tecnologia Turbo Boost?
Não. Se o processador tem uma invariante TSC , o QPC não é afectada por este tipo de alterações. Se o processador não tem um TSC invariante, QPC reverterá para um temporizador de hardware plataforma que não será afetado por mudanças frequência do processador ou tecnologia Turbo Boost.

O QPC trabalho de forma confiável em sistemas multi-processador, sistema multi-core e sistemas com hyper-threading?
Sim

Como faço para determinar e validar que QPC funciona na minha máquina?
Você não precisa executar tais verificações.

Quais processadores têm TSCs não invariantes? [..Read mais ..] "

Mas se você não precisa a precisão do cronômetro () ou pelo menos quer saber exatamente sobre o desempenho do cronômetro (vs. estática baseada exemplo) e outras variantes possíveis, continue lendo:

eu assumi a referência acima cskwg, e estendeu o código para mais variantes. Tenho medido com um alguns anos i7 4700 MQ e C # 7 com o VS 2017 (para ser mais preciso, compilado com .NET 4.5.2, apesar literais binários, é C # 6 (usado desta: string literais e 'usando estática '). Especialmente no cronometro () desempenho parece ser melhorada em comparação com a mencionada referência.

Este é um exemplo de resultados de 10 milhões de repetições em um loop, como sempre, os valores absolutos não são importantes, mas até mesmo os valores relativos podem diferir em outro hardware:

32 bit, modo de versão sem otimização:

Medido: GetTickCount64 () [ms]: 275
Medidos: Environment.TickCount [ms]: 45
Medido: DateTime.UtcNow.Ticks [ms]: 167
Medidos: Cronômetro: .ElapsedTicks [ms]: 277
Medidos: Cronômetro: .ElapsedMilliseconds [ms]: 548
Medidos: estáticos Stopwatch.GetTimestamp [ms]: 193
Medidos: Cronômetro + conversão para DateTime [ms]: 551
Compare isso com DateTime.Now.Ticks [ms]: 9010

, o modo de lançamento 32 bit, otimizado:

Medido: GetTickCount64 () [ms]: 198
Medidos: Environment.TickCount [ms]: 39
Medido: DateTime.UtcNow.Ticks [ms]: (!) 66
Medidos: Cronômetro: .ElapsedTicks [ms]: 175
Medidos: Cronômetro: .ElapsedMilliseconds [ms]: 491
Medidos: estáticos Stopwatch.GetTimestamp [ms]: 175
Medido: conversão Cronômetro + para DateTime [ms]: 510
Compare isso com DateTime.Now.Ticks [ms]: 8460

64 bit, modo de versão sem otimização:

Medido: GetTickCount64 () [ms]: 205
Medidos: Environment.TickCount [ms]: 39
Medido: DateTime.UtcNow.Ticks [ms]: 127
Medidos: Cronômetro: .ElapsedTicks [ms]: 209
Medidos: Cronômetro: .ElapsedMilliseconds [ms]: 285
Medidos: estáticos Stopwatch.GetTimestamp [ms]: 187
Medidos: Cronômetro + conversão para DateTime [ms]: 319
Compare isso com DateTime.Now.Ticks [ms]: 3040

, o modo de lançamento 64 bit, otimizado:

Medido: GetTickCount64 () [ms]: 148
Medidos: Environment.TickCount [ms]: 31 (é ainda vale a pena?)
Medido: DateTime.UtcNow.Ticks [ms]: (!) 76
Medidos: Cronômetro: .ElapsedTicks [ms]: 178
Medidos: Cronômetro: .ElapsedMilliseconds [ms]: 226
Medidos: estáticos Stopwatch.GetTimestamp [ms]: 175
Medidos: Cronômetro + conversão para DateTime [ms]: 246
Compare isso com DateTime.Now.Ticks [ms]: 3020

Pode ser muito interessante, que a criação de um valor DateTime para imprimir o tempo Cronômetro parece ter quase sem custos . Interessante de uma forma mais acadêmica do que a maneira prática é que estática cronômetro é ligeiramente mais rápido (como esperado). Alguns pontos de otimização são bastante interessantes. Por exemplo, eu não posso explicar por que Stopwatch.ElapsedMilliseconds única com 32 bit é tão lento comparado a ele é outras variantes, por exemplo, a um estático. Este e DateTime.Now mais que o dobro sua velocidade com 64 bits.

Você pode ver: Apenas para milhões de execuções, o tempo de cronômetro começa a matéria. Se este for realmente o caso (mas cuidado com micro-otimização muito cedo), pode ser interessante que, com GetTickCount64 (), mas especialmente com DateTime.UtcNow , você tem um temporizador 64 bit (longo) com menos precisão do que cronômetro, mas mais rápido, de modo que você não tem que mexer com o de 32 bits "feio" Environment.TickCount.

Como esperado, DateTime.Now é de longe o mais lento de todos.

Se você executá-lo, os recupera código também a sua precisão do cronômetro atual e muito mais.

Aqui está o código de referência completo:

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

Você deve usar a Cronômetro classe em vez.

Eu uso Environment.TickCount porque:

  1. A classe Cronômetro não está no Compact Framework.
  2. Cronômetro usa o mesmo mecanismo de tempo subjacente como TickCount, por isso os resultados não vai ser mais ou menos preciso.
  3. O problema wrap-around com TickCount é cosmicamente improvável que seja hit (você teria que deixar seu computador funcionando por 27 dias e, em seguida, tentar medir um tempo que só acontece para abranger o envoltório -around momento), e mesmo se você tivesse acabado com isso o resultado seria um enorme intervalo de tempo negativa (de modo que seria tipo de destacar).

Dito isto, eu também recomendo o uso do cronômetro, se ele está disponível para você. Ou você poderia ter cerca de 1 minuto e escrever um cronômetro-like classe que envolve Environment.TickCount.

BTW, não vejo nada na documentação do cronômetro que menciona o problema wrap-around com o mecanismo temporizador subjacente, então eu não ficaria surpreso em tudo para descobrir que sofre Cronômetro do mesmo problema. Mas, novamente, eu não iria gastar algum tempo se preocupando com isso.

Eu ia dizer envolvê-la em uma classe cronômetro, mas Grzenio já disse a coisa certa, por isso vou dar-lhe um pequeno aumento. Tais fatores encapsulamento fora a decisão sobre qual caminho é melhor, e isso pode mudar com o tempo. Lembro-me de estar chocado com o quão caro pode estar recebendo o tempo em alguns sistemas, assim que ter um lugar que pode implementar a melhor técnica pode ser muito importante.

Para tempo one-shot, é ainda mais simples de escrever

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

Eu acho que a envolvente cosmicamente improvável no TickCount é ainda uma preocupação menor para cronômetro, dado que o campo ElapsedTicks é um longo. Na minha máquina, cronômetro é de alta resolução, em 2.4e9 carrapatos por segundo. Mesmo com essa taxa, levaria mais de 121 anos para transbordar o campo carrapatos. Claro, eu não sei o que está acontecendo debaixo das cobertas, de modo que ter com um grão de sal. No entanto, noto que a documentação para StopWatch nem sequer menciona a questão envolvente, enquanto o doc para TickCount faz.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top