Pergunta

Estou a portar um jogo, que foi originalmente escrito para a API do Win32, Linux (bem, portar o OS X porta do Win32 porta para Linux).

Eu tenho implementado QueryPerformanceCounter dando o uSeconds desde o início do processo:

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Este, juntamente com QueryPerformanceFrequency() dando uma constante 1000000 como a frequência, funciona bem na minha máquina, me dando um de 64 bits variável que contém uSeconds desde o seu início.

Então, é este portátil? Eu não quero descobrir o que funciona de forma diferente se o kernel foi compilado em uma determinada forma ou qualquer coisa assim.Eu estou bem com ele sendo não-portáteis para algo diferente do que o Linux, no entanto.

Foi útil?

Solução

Talvez.Mas você tem problemas maiores. gettimeofday() pode resultar em incorretas intervalos se há processos no seu sistema que altere o timer (ou seja, o ntpd).Em um "normal" do linux, porém, eu acredito que a resolução de gettimeofday() é 10us.Ele pode saltar para a frente e para trás e de tempo, consequentemente, com base nos processos em execução no sistema.Isso efetivamente faz com que a resposta à sua pergunta, não.

Você deve olhar para clock_gettime(CLOCK_MONOTONIC) para intervalos de tempo.Ele sofre de vários problemas de menor devido a coisas como sistemas multi-core e externo definições do relógio.

Além disso, olhar para a clock_getres() função.

Outras dicas

De alta Resolução, Baixo Overhead de Sincronismo para Processadores Intel

Se você está em hardware Intel, veja como ler a CPU em tempo real da instrução de contador.Ele vai dizer-lhe o número de ciclos de CPU executados, uma vez que o processador foi inicializado.Este é provavelmente o melhor grão de contador que você pode obter para medição de desempenho.

Note que este é o número de ciclos de CPU.No linux, você pode obter a velocidade da CPU /proc/cpuinfo e dividem-se para obter o número de segundos.Convertendo isso para um casal é bastante útil.

Quando eu executar esse na minha caixa, eu recebo

11867927879484732
11867927879692217
it took this long to call printf: 207485

Aqui está o Guia do desenvolvedor Intel que dá toneladas de detalhe.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}

@Bernardo:

Eu tenho que admitir, a maioria de seu exemplo foi direto em cima de minha cabeça.Ele não compile, e parece funcionar, embora.É esta segurança para sistemas SMP ou SpeedStep?

Essa é uma boa pergunta...Eu acho que o código está ok.Do ponto de vista prático, podemos usá-lo em minha companhia todos os dias, e corremos em uma grande variedade de caixas, tudo a partir de 2 a 8 núcleos.Claro, YMMV, etc, mas parece ser confiável e de baixa sobrecarga (porque não fazer um parâmetro de contexto do sistema-espaço) método a tempo.

Geralmente ele funciona da seguinte maneira:

  • declarar o bloco de código assembler (e voláteis, de modo que o otimizador vai deixá-lo sozinho).
  • executar a instrução CPUID.Além de obter algumas informações sobre a CPU (o que nós não fazemos nada com) sincroniza o CPU execução do buffer para que os intervalos não são afetados por fora-de-ordem de execução.
  • executar o rdtsc (leia carimbo de data / hora) de execução.Este obtém o número de máquina de ciclos executados, uma vez que o processador foi redefinida.Esta é uma versão de 64-bit valor, portanto, com a atual velocidade da CPU ele vai embrulhar em torno de todos os 194 anos ou assim.Curiosamente, no original Pentium referência, eles observação que envolve todos os 5800 anos ou assim.
  • o último par de linhas de armazenar os valores de registradores em as variáveis hi e lo, e coloque-a em 64 bits, o valor de retorno.

Notas específicas:

  • fora-de-ordem de execução pode causar resultados incorretos, por isso vamos executar o "cpuid" de instrução que, além de lhe dar algumas informações sobre a cpu também sincroniza qualquer fora-de-ordem de execução das instruções.

  • A maioria dos sistemas operacionais sincronizar os contadores no CPUs quando eles começam, então, a resposta é boa para dentro de um par de nano-segundos.

  • O modo de hibernação comentário é provavelmente verdade, mas na prática você provavelmente não se importa com os tempos entre o modo de hibernação limites.

  • em relação speedstep:Mais recentes CPUs Intel compensar a velocidade alterações e retorna uma contagem ajustada.Eu fiz uma pesquisa rápida sobre algumas das caixas em nossa rede e encontrei apenas um caixa que não tem isso:um Pentium 3 a execução de alguns antigo servidor de banco de dados.(estas são linux caixas, então eu verifiquei com:grep constant_tsc /proc/cpuinfo)

  • Eu não tenho certeza sobre as CPUs AMD, estamos principalmente um Intel loja, embora eu saiba que alguns dos nossos baixo nível de sistemas de gurus fez um AMD avaliação.

Espero que este satisfaz a sua curiosidade, é um interessante e (IMHO) sob estudado área de programação.Você sabe, quando Jeff e Joel foram falando sobre se ou não um programador deve saber C?Eu era gritando com eles, "hey se esqueça de que o alto nível C coisas...montador é o que você deve aprender, se você quer saber o que o computador está fazendo!"

Você pode estar interessado em Linux FAQ para clock_gettime(CLOCK_REALTIME)

O vinho é, na verdade, usando gettimeofday() para implementar QueryPerformanceCounter() e é conhecido por fazer muitos jogos do Windows funcionam no Linux e Mac.

Começa http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

leva a http://source.winehq.org/source/dlls/ntdll/time.c#L448

Por isso diz microssegundos explicitamente, mas diz que a resolução do relógio do sistema é indeterminado.Eu suponho que a resolução significa, neste contexto, como a menor quantidade que venha a ser incrementado?

A estrutura de dados é definido como tendo microssegundos como uma unidade de medida, mas isso não significa que o relógio ou o sistema operacional é realmente capaz de medir o que finamente.

Como as outras pessoas têm sugerido, gettimeofday() é ruim porque a definição do tempo pode causar inclinação do relógio e jogar fora o seu cálculo. clock_gettime(CLOCK_MONOTONIC) é o que você quer, e clock_getres() vai contar a precisão do seu relógio.

A resolução real de gettimeofday() depende da arquitetura de hardware.Processadores Intel, bem como SPARC máquinas oferecem temporizadores de alta resolução que medida microssegundos.Outras arquiteturas de hardware cair de volta para o sistema do temporizador, que normalmente é definido para 100 Hz.Em tais casos, o tempo de resolução vai ser menos precisas.

Obtive esta resposta de Alta Resolução de Medição de Tempo e contadores de duração, Parte I

Esta resposta menciona problemas com o relógio que está a ser ajustado.Ambos os seus problemas, garantindo unidades de escala e os problemas com o tempo que está a ser ajustado são resolvidos em C++11, com o <chrono> a biblioteca.

O relógio std::chrono::steady_clock é garantia de não ser ajustados, e, além disso, ele avança a uma taxa constante em relação ao tempo real, assim, tecnologias como SpeedStep não deve afetá-lo.

Você pode obter typesafe unidades convertendo para um dos std::chrono::duration especializações, tais como std::chrono::microseconds.Com esse tipo, não há ambigüidade sobre as unidades utilizadas pelo valor de escala.No entanto, tenha em mente que o relógio não tem necessariamente esta resolução.Você pode converter uma duração para attoseconds sem precisar de um relógio preciso.

A partir de minha experiência, e do que tenho lido na internet, a resposta é "Não", isso não é garantido.Depende da velocidade da CPU, sistema operativo, versão do Linux, etc.

Lendo o RDTSC não é confiável em sistemas SMP, uma vez que cada CPU mantém o seu próprio contador e cada contador não é garantido por sincronizados com relação a outra CPU.

Eu poderia sugerir a tentar clock_gettime(CLOCK_REALTIME).O posix manual indica que esta deve ser implementada em todos os sistemas compatíveis com.Ele pode fornecer um nanosegundo conta, mas você provavelmente vai querer verificar para clock_getres(CLOCK_REALTIME) em seu sistema para ver que a resolução real é.

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