Environment.TickCount vs DateTime.Now

É sempre bom usar o Environment.TickCount para calcular intervalos de tempo?

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

Porque o TickCount é assinado e vai rolar após 25 dias (leva 50 dias para acertar todos os 32 bits, mas você tem que refazer o bit assinado se você quiser fazer algum sentido da matemática), parece ser muito arriscado ser útil .

Eu estou usando DateTime.Now vez disso. Essa é a melhor maneira de fazer isso?

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

Use a class de 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; 

Environment.TickCount é baseado na function WinAPI GetTickCount () . É em milissegundos, mas a precisão real é de cerca de 15,6 ms. Então você não pode medir intervalos de tempo mais curtos (ou você receberá 0)

Nota: O valor retornado é Int32, portanto, esse contador passa por cada ~ 49,7 dias. Você não deve usá-lo para medir intervalos tão longos.

DateTime.Ticks é baseado na function WinAPI GetSystemTimeAsFileTime (). É em 100 nanossegundos (décimos de microssegundos). A precisão real de DateTime.Ticks depende do sistema. No XP, o incremento do clock do sistema é de aproximadamente 15,6 ms, o mesmo que em Environment.TickCount. No Windows 7, sua precisão é de 1 ms (enquanto o Environemnt.TickCount ainda é de 15,6 ms), no entanto, se um esquema de economia de energia for usado (geralmente em laptops), ele também pode chegar a 15,6 ms.

O cronômetro é baseado na function QueryPerformanceCounter () WinAPI (mas, se o contador de desempenho de alta resolução não for suportado pelo sistema, o DateTime.Ticks será usado)

Antes de usar o aviso do StopWatch, dois problemas:

  • pode não ser confiável em sistemas multiprocessadores (consulte MS kb895980 , kb896256 )
  • pode não ser confiável se a freqüência da CPU variar (leia este artigo)

Você pode avaliar a precisão em seu sistema com um 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 a rolagem? Contanto que a duração que você está medindo esteja abaixo de 24,9 dias e você calcule a duração relativa , você está bem. Não importa há quanto tempo o sistema está em execução, contanto que você se preocupe apenas com a sua parte desse tempo de execução (em oposição a realizar diretamente comparações menor que ou maior do que nos pontos inicial e final). 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, deixa a CPU lidar com isso.

Esta é uma situação comum em que me deparei antes, com contagens de tempo em sistemas embarcados. Eu nunca compararia beforerollover

Você provavelmente deseja System.Diagnostics.StopWatch .

Se você está procurando a funcionalidade do Environment.TickCount mas sem a sobrecarga de criar novos objects Stopwatch , é possível usar o método estático Stopwatch.GetTimestamp() (junto com Stopwatch.Frequency ) para calcular períodos longos. Como GetTimestamp() retorna um long , ele não vai GetTimestamp() por muito, muito tempo (mais de 100.000 anos, na máquina que estou usando para escrever isso). Também é muito mais preciso que o Environment.TickCount que tem uma resolução máxima de 10 a 16 milissegundos.

Usar

 System.Diagnostics.Stopwatch 

Tem uma propriedade chamada

 EllapsedMilliseconds 

Environment.TickCount parece ser muito mais rápido que as outras soluções:

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

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

 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 está uma espécie de resumo atualizado e actualizado das respostas e comentários mais úteis neste tópico + referências e variantes adicionais:

Primeira coisa: Como outros apontaram nos comentários, as coisas mudaram nos últimos anos e com o Windows “moderno” (Win XP ++) e .NET, e o hardware moderno não existem ou existem poucas razões para não usar o Stopwatch (). Veja MSDN para detalhes. Citações:

“A precisão do QPC é afetada pelas mudanças de freqüência do processador causadas pelo gerenciamento de energia ou tecnologia Turbo Boost?
Não. Se o processador tiver um TSC invariante , o QPC não será afetado por esse tipo de alteração. Se o processador não tiver um TSC invariável, o QPC será revertido para um timer de hardware de plataforma que não será afetado pelas alterações de freqüência do processador ou pela tecnologia Turbo Boost.

O QPC funciona de maneira confiável em sistemas com vários processadores, sistema multi-core e sistemas com hyper-threading?
sim

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

Quais processadores possuem TSCs não-invariantes? [.. Leia mais ..] ”

Mas se você não precisa da precisão do cronômetro () ou pelo menos quiser saber exatamente sobre o desempenho do cronômetro (estático vs. baseado em instância) e outras variantes possíveis, continue lendo:

Eu assumi o benchmark acima do cskwg e estendi o código para mais variantes. Eu tenho medido com alguns anos i7 4700 MQ e C # 7 com VS 2017 (para ser mais preciso, compilado com o .NET 4.5.2, apesar de literais binários, é C # 6 (usado deste: string literais e ‘usando estática Particularmente, o desempenho do cronômetro () parece ser melhorado em comparação com o benchmark mencionado.

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 bits, modo de lançamento sem otimização:

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

32 bits, modo de lançamento, otimizado:

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

64 bits, modo de lançamento sem otimização:

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

64 bits, modo de lançamento, otimizado:

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

Pode ser muito interessante, que criar um valor DateTime para imprimir o tempo do cronômetro parece ter quase nenhum custo . Interessante de uma forma mais acadêmica do que prática é que o cronômetro estático é um pouco mais rápido (como esperado). Alguns pontos de otimização são bem interessantes. Por exemplo, não consigo explicar por que o Stopwatch.ElapsedMilliseconds apenas com 32 bits é tão lento comparado a outras variantes, por exemplo, a estática. Isso e DateTime.Now mais que dobram sua velocidade com 64 bits.

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

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

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

Aqui está o código completo de benchmark:

 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, eg 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 class Stopwatch .

Eu uso Environment.TickCount porque:

  1. A class de cronômetro não está no Compact Framework.
  2. O cronômetro usa o mesmo mecanismo de temporização subjacente que o TickCount, para que os resultados não sejam mais ou menos precisos.
  3. O problema envolvente com TickCount é cosmicamente improvável de ser atingido (você teria que deixar seu computador funcionando por 27 dias e, em seguida, tentar medir um tempo que apenas acontece para abranger o momento envolvente), e mesmo se você fez acertar o resultado seria um enorme intervalo de tempo negativo (por isso, se destacaria).

Dito isto, também recomendo usar o cronômetro, se estiver disponível para você. Ou você pode levar cerca de 1 minuto e escrever uma class parecida com um cronômetro que envolve o Environment.TickCount.

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

Eu ia dizer que é um cronômetro, mas Grzenio já disse a coisa certa, então vou dar um aumento. Tal encapsulamento desencadeia a decisão sobre qual é o melhor caminho, e isso pode mudar no tempo. Lembro-me de ficar chocado com o quão caro pode ser a obtenção de tempo em alguns sistemas, por isso ter um local que possa implementar a melhor técnica pode ser muito importante.

Para um momento único, é ainda mais simples escrever

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

Eu acho que o envolvente cosmicamente improvável no TickCount é ainda menos preocupante para o StopWatch, dado que o campo ElapsedTicks é longo. Na minha máquina, o StopWatch é de alta resolução, a 2.4e9 ticks por segundo. Mesmo a essa taxa, levaria mais de 121 anos para transbordar o campo de carrapatos. Claro, eu não sei o que está acontecendo debaixo das cobertas, então leve isso com um grão de sal. No entanto, percebo que a documentação do StopWatch nem sequer menciona o problema de encadernação, enquanto o documento do TickCount funciona.