Melhor maneira de obter tempo decorrido em milissegundos no Windows

8

Estou tentando fazer isso usando dois FILETIMEs, convertendo-os para ULONGLONGs, subtraindo os ULONGLONGs e dividindo o resultado por 10000. Mas é bem lento e quero saber se existe uma maneira melhor de fazer isso.Eu uso c ++ com o visual studio 2008 express edition. Isto é o que estou usando:

FILETIME filetime,filetime2;
GetSystemTimeAsFileTime(&filetime);
Sleep(100);
GetSystemTimeAsFileTime(&filetime2);
ULONGLONG time1,time2;
time1 = (((ULONGLONG) filetime.dwHighDateTime) << 32) + filetime.dwLowDateTime;
time2 = (((ULONGLONG) filetime2.dwHighDateTime) << 32) + filetime2.dwLowDateTime;
printf("ELAPSED TIME IN MS:%d",(int)((time2-time1)/10000));
XaitormanX
fonte

Respostas:

11

Use QueryPerformanceCounter .

long long milliseconds_now() {
    static LARGE_INTEGER s_frequency;
    static BOOL s_use_qpc = QueryPerformanceFrequency(&s_frequency);
    if (s_use_qpc) {
        LARGE_INTEGER now;
        QueryPerformanceCounter(&now);
        return (1000LL * now.QuadPart) / s_frequency.QuadPart;
    } else {
        return GetTickCount();
    }
}

// Somewhere else...
    // ...
    long long start = milliseconds_now();
    // ....
    long long elapsed = milliseconds_now() - start;

fonte
E isso correria rápido?
precisa saber é o seguinte
Rápido o suficiente para ligar uma dúzia de vezes durante um quadro sem impacto perceptível. Quantas vezes você espera precisar?
cerca de 4 vezes por quadro. Mas se eu colocar esse código em um loop para fazê-lo 100 vezes, leva cerca de 10 segundos para executar, o que é lento, por que isso está acontecendo?
XaitormanX
11
Você tem um Sleep(100)no seu código. Não tem nada a ver com os temporizadores.
2
Ele rodou rápido em uma p90 em 1996; Eu acho que vai correr rápido o suficiente hoje. ;)
Maximus Minimus 04/04
7

Geralmente, timeGetTime () é melhor para a lógica do jogo de temporização - GetTickCount não possui resolução suficientemente alta e QPC e RDTSC são muito mais problemas do que valem para esse fim.

Por outro lado, para criação de perfil, RDTSC ou QPC pode valer a pena. Eu prefiro RDTSC sobre QPC, embora a Microsoft recomende QPC.

As quatro funções de tempo comuns que eu uso no win32:

GetTickCount () retorna o tempo atual em milissegundos em relação a algum zero arbitrário (geralmente, embora nem sempre o tempo de inicialização do sistema), como um número inteiro de 32 bits (para que ocorra a cada 49 dias). Geralmente é o método mais rápido para medir um tempo. Infelizmente, é de baixa resolução - geralmente atualiza apenas 64 vezes por segundo. Ao contrário do boato popular, chamar timeBeginPeriod (1) não aumentará sua resolução em nenhum sistema que eu tenha testado. Há também uma variante de 64 bits, se você estiver preocupado com a quebra.

timeGetTime () retorna o tempo atual, em milissegundos, relativo a algum zero arbitrário, como um valor de 32 bits (para que ocorra após 49 dias ou mais). Não é tão rápido quanto GetTickCount, mas ainda é bastante razoável. Pode ser preciso em um único milissegundo, embora isso possa exigir a chamada timeBeginPeriod (1) primeiro. O uso do timeGetTime requer a vinculação ao winmm.lib e a inclusão do Mmsystem.h.

QueryPerformanceCounter () retorna o horário atual em unidades arbitrárias como um número inteiro de 64 bits. Você pode descobrir quais são as unidades chamando QueryPerformanceFrequency () e as unidades não serão alteradas (na ausência de uma reinicialização). A implementação subjacente disso varia muito, e cada implementação possui suas próprias peculiaridades e erros que podem ocorrer em algum hardware, tornando isso desagradável para uso em aplicativos amplamente implantados. Algumas implementações são muito lentas, outras são razoáveis, embora nenhuma seja tão rápida quanto GetTickCount ou mesmo timeGetTime. Normalmente, a resolução de tempo é melhor que 1 microssegundo, embora não necessariamente muito melhor.

RDTSC é um código de operação da linguagem assembly x86 / x64 que retorna o tempo atual em unidades de ciclos da CPU (ou seja, como um número inteiro de 64 bits. Em alguns compiladores, ele pode ser acessado como um intrínseco (__rdstc), em outros você precisará de asm inline. Sua velocidade varia um pouco entre as CPUs, mas geralmente é bastante razoável - geralmente significativamente mais rápida que QueryPerformanceCounter, mas não tão rápida quanto GetTickCount.Infelizmente, existem algumas peculiaridades - não tantas quanto QueryPerformanceCounter, mas ainda muito mais que as funções de tempo de resolução mais baixa. Às vezes, pode ser executado em versões anteriores em CPUs multicore quando você alterna núcleos devido à sincronização imperfeita entre os núcleos, as unidades são difíceis de descobrir e as unidades podem mudar no meio do tempo devido ao gerenciamento de energia alterando a taxa do clock da CPU.

Observe que cada um desses quatro métodos pode ser desviado em relação aos outros três - não há um fluxo de tempo claro e autorizado.

user3535668
fonte
3

Para responder à sua pergunta: não há "melhor" solução para medir o tempo. Todo método tem suas próprias vantagens / desvantagens. Tudo depende de suas necessidades.

Aqui estão várias perguntas que você deve se perguntar antes de escolher uma solução:

  • resolução do temporizador . Você realmente precisa de um cronômetro preciso (<1ms) ou pode viver com algo menos preciso (por exemplo: precisão de 10ms)?

  • custo da CPU . alguns métodos exigirão mais CPU do que outros. temporizadores geralmente mais precisos requerem mais CPU. Além disso, a chamada de alguns métodos pode ter consequências para outras aplicações (por exemplo: a chamada timeBeginPeriod(1)no win32 estressa o sistema operacional e reduz a vida da bateria do laptop)

  • trabalha com SMP . alguns métodos usam dados específicos da CPU (por exemplo: número de ciclos decorridos) e podem ter alguns problemas em ambientes com multiprocessadores.

  • funciona com CPU com recurso de economia de energia. Algumas CPUs têm a capacidade de alterar a frequência ao longo do tempo, o que pode causar alguns problemas em alguns métodos de temporização.

Para o exemplo que você dá, eu usaria uma combinação de QueryPerformanceCounter() / QueryPerformanceFrequency().

Observe também que no exemplo que você dá, sleep(100)na verdade, pode esperar mais de 100 ms. Assim, mesmo com um timer muito preciso, ele pode retornar alguns resultados além de 100ms.

tigrou
fonte
Embora seja verdade que alguns métodos de temporização tenham problemas no SMP, esses sempre são erros no HAL, BIOS ou outros problemas relacionados a drivers e devem ser corrigidos nesse nível. É raro que você possa resolver o problema mudando para um método de tempo diferente. Por exemplo, eu vi o QPC rodar de trás para frente por causa de drivers ruins, mas o GetTickCount também o faria - o único motivo disso não ocorreu porque o salto foi de ~ 50µsec, por isso não notou nada. Na melhor das hipóteses, seu aplicativo pode fazer uma maxchamada com o valor retornado anteriormente.
No momento, o QPC e funções equivalentes em outros sistemas operacionais, como o POSIX clock_gettime, são todos especificados para não serem executados de maneira inversa, mesmo em sistemas SMP, e independentemente da frequência atual da CPU. Portanto, é um mau conselho alertar as pessoas para longe delas e para funções de temporização piores, porque qualquer outra função de temporização provavelmente sofreria os mesmos problemas se você conseguisse fazê-las funcionar com a precisão que você queria da QPC em primeiro lugar. Então, sim, há é uma melhor solução: QPC, clock_gettime e mach_absolute_time. Para o tempo de loop do jogo, não há razão para usar qualquer outra coisa, se disponível.
0

Acabei de fazer alguns benchmarks e praticamente não há mais uma boa razão para usar o timeGetTime. QueryPerformanceCounter é um pouco mais rápido e muito mais preciso.

Depois, há GetTickCount, que é quase instantâneo, pois tudo o que faz é reler algum valor que o sistema operacional altera durante o agendamento do processo. O valor permanece o mesmo por mais de 15ms por vez e não é recomendado para nada além de um tempo muito grosseiro.

Dwedit
fonte
-1

Dê uma olhada na função GetTickCount () , seu valor de retorno é o número de milissegundos decorridos desde que o sistema foi iniciado.

unsigned int Last = 0;
unsigned int Now = 0;
Last = GetTickCount();
//...Run your functions..
Now = GetTickCount();
printf("Elapsed Time in Milliseconds: %i",(Now-Last));
bandrewk
fonte
6
-1. GetTickCount () tem baixa resolução.