C ++ obtendo milissegundos de tempo no Linux - clock () não parece funcionar corretamente

102

No Windows, clock()retorna o tempo em milissegundos, mas nesta máquina Linux em que estou trabalhando, ele o arredonda para o 1000 mais próximo, de modo que a precisão é apenas para o "segundo" nível e não para o nível de milissegundos.

Eu encontrei uma solução com o Qt usando a QTimeclasse, instanciando um objeto e chamando start()em seguida, chamando elapsed()para obter o número de milissegundos decorridos.

Tive sorte porque estou trabalhando com Qt para começar, mas gostaria de uma solução que não dependa de bibliotecas de terceiros,

Não existe uma maneira padrão de fazer isso?

ATUALIZAR

Por favor, não recomende Boost ..

Se Boost e Qt podem fazer isso, certamente não é mágica, deve haver algo padrão que eles estão usando!

Hassen
fonte
2
Sobre a edição - mas fazê-lo de forma portátil é um pouco chato.
Anônimo

Respostas:

37

Você pode usar gettimeofday no início e no final do seu método e, em seguida, diferenciar as duas estruturas de retorno. Você obterá uma estrutura como a seguinte:

struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
}

EDITAR: Como os dois comentários abaixo sugerem, clock_gettime (CLOCK_MONOTONIC) é uma escolha muito melhor se você o tiver disponível, que deve estar em quase todos os lugares atualmente.

EDIT: Alguém comentou que você também pode usar C ++ moderno com std :: chrono :: high_resolution_clock, mas isso não é garantido como monotônico. Em vez disso, use stable_clock.

Adam Hawes
fonte
38
terrível para um trabalho sério. Grandes problemas duas vezes por ano, quando alguém namora e, claro, sincronização NTP. Use clock_gettime (CLOCK_MONOTONIC,)
AndrewStone
9
@AndrewStone: o horário UNIX não muda duas vezes por ano. Ou mesmo uma vez por ano. Mas, sim, CLOCK_MONOTONICé ótimo para evitar ajustes de hora do sistema localizados.
Lightness Races in Orbit
138
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    struct timeval start, end;

    long mtime, seconds, useconds;    

    gettimeofday(&start, NULL);
    usleep(2000);
    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

    printf("Elapsed time: %ld milliseconds\n", mtime);

    return 0;
}
CTT
fonte
4
Por que você adiciona +0,5 à diferença?
Mahmoud Al-Qudsi de
19
@Computer Guru, é uma técnica comum para arredondar valores positivos. Quando o valor é truncado para um valor inteiro, qualquer coisa entre 0,0 e 0,4999 ... antes da adição fica truncado para 0, e entre 0,5 e 0,9999 ... fica truncado para 1.
Mark Ransom
8
tv_usec não é milissegundos, é microcsegundos.
NebulaFox,
13
terrível para um trabalho sério. Grandes problemas duas vezes por ano, quando alguém namora -s e, claro, sincronização NTP
AndrewStone
14
@AndrewStone está certo, use clock_gettime (2) com CLOCK_REALTIME para comparar tempos no mesmo computador. Na página de manual gettimeofday (2): POSIX.1-2008 marks gettimeofday() as obsolete, recommending the use of clock_gettime(2) instead. @CTT, você poderia atualizar o exemplo alterando struct timevalpara struct timespece gettimeofday(&start, NULL)para clock_gettime(CLOCK_MONOTONIC, &start)para que as pessoas não tenham problemas?
Bobby Powers
58

Por favor note que clockse não medir o tempo de relógio de parede. Isso significa que se o seu programa leva 5 segundos, clocknão medirá 5 segundos necessariamente, mas poderá mais (seu programa pode executar vários threads e, portanto, pode consumir mais CPU do que o tempo real) ou menos. Ele mede uma aproximação do tempo de CPU usado. Para ver a diferença, considere este código

#include <iostream>
#include <ctime>
#include <unistd.h>

int main() {
    std::clock_t a = std::clock();
    sleep(5); // sleep 5s
    std::clock_t b = std::clock();

    std::cout << "difference: " << (b - a) << std::endl;
    return 0;
}

Produz no meu sistema

$ difference: 0

Porque tudo o que fizemos foi dormir e não usar o tempo da CPU! No entanto, usando gettimeofdayobtemos o que queremos (?)

#include <iostream>
#include <ctime>
#include <unistd.h>
#include <sys/time.h>

int main() {
    timeval a;
    timeval b;

    gettimeofday(&a, 0);
    sleep(5); // sleep 5s
    gettimeofday(&b, 0);

    std::cout << "difference: " << (b.tv_sec - a.tv_sec) << std::endl;
    return 0;
}

Saídas em meu sistema

$ difference: 5

Se você precisa de mais precisão, mas deseja obter o tempo da CPU , pode considerar o uso da getrusagefunção.

Johannes Schaub - litb
fonte
⁺¹ sobre a menção a sleep()- Já pensei em fazer uma pergunta (por que funciona bem para todos, exceto para mim ?!) , quando encontrei sua resposta.
Hi-Angel
18

Também recomendo as ferramentas oferecidas pelo Boost. Ou o Boost Timer mencionado, ou hackear algo fora do Boost.DateTime ou há uma nova biblioteca proposta na sandbox - Boost.Chrono : Este último será um substituto para o Timer e terá:

  • Os utilitários de tempo da Biblioteca padrão C ++ 0x, incluindo:
    • Modelo de classe duration
    • Modelo de classe time_point
    • Relógios:
      • system_clock
      • monotonic_clock
      • high_resolution_clock
  • Modelo de classe timer, com typedefs:
    • system_timer
    • monotonic_timer
    • high_resolution_timer
  • Relógios e cronômetros de processo:
    • process_clock, capturando tempos reais, da CPU do usuário e da CPU do sistema.
    • process_timer, capturando tempos reais decorridos, da CPU do usuário e da CPU do sistema.
    • run_timer, relatórios convenientes de | process_timer | resultados.
  • A aritmética racional de tempo de compilação da Biblioteca Padrão C ++ 0x.

Aqui está a fonte da lista de recursos

Anônimo
fonte
Por enquanto, você pode usar o Boost Timer e então migrar para o Chrono quando for revisado / aceito.
Anônimo
13

Escrevi uma Timeraula com base na resposta dos CTT . Pode ser usado da seguinte maneira:

Timer timer = Timer();
timer.start();
/* perform task */
double duration = timer.stop();
timer.printTime(duration);

Aqui está sua implementação:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
using namespace std;

class Timer {
private:

    timeval startTime;

public:

    void start(){
        gettimeofday(&startTime, NULL);
    }

    double stop(){
        timeval endTime;
        long seconds, useconds;
        double duration;

        gettimeofday(&endTime, NULL);

        seconds  = endTime.tv_sec  - startTime.tv_sec;
        useconds = endTime.tv_usec - startTime.tv_usec;

        duration = seconds + useconds/1000000.0;

        return duration;
    }

    static void printTime(double duration){
        printf("%5.6f seconds\n", duration);
    }
};
Chris Redford
fonte
2
Isso é legal, mas os "nsegundos" são enganosos porque timeval não armazena nanossegundos, mas sim microssegundos, então eu sugiro que as pessoas chamem isso de "useconds".
pho0 de
Obrigado. Correção feita.
Chris Redford
9

Se você não precisa que o código seja portátil para unidades antigas, você pode usar clock_gettime (), que fornecerá o tempo em nanossegundos (se o seu processador suportar essa resolução). É POSIX, mas de 2001.

Zachary Hamm
fonte
4

clock () geralmente tem uma resolução muito ruim. Se você quiser medir o tempo no nível dos milissegundos, uma alternativa é usar clock_gettime (), conforme explicado nesta pergunta.

(Lembre-se de que você precisa fazer um link com -lrt no Linux).

JesperE
fonte
4

Com C ++ 11 e std::chrono::high_resolution_clockvocê pode fazer isso:

#include <iostream>
#include <chrono>
#include <thread>
typedef std::chrono::high_resolution_clock Clock;

int main()
{
    std::chrono::milliseconds three_milliseconds{3};

    auto t1 = Clock::now();
    std::this_thread::sleep_for(three_milliseconds);
    auto t2 = Clock::now();

    std::cout << "Delta t2-t1: " 
              << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count()
              << " milliseconds" << std::endl;
}

Resultado:

Delta t2-t1: 3 milliseconds

Link para a demonstração: http://cpp.sh/2zdtu

Martin G
fonte
2

clock () não retorna milissegundos ou segundos no linux. Normalmente, clock () retorna microssegundos em um sistema Linux. A maneira correta de interpretar o valor retornado por clock () é dividi-lo por CLOCKS_PER_SEC para descobrir quanto tempo se passou.

SoapBox
fonte
não está na caixa em que estou trabalhando! Além disso, estou dividindo por CLOCKS_PER_SEC, mas é inútil porque a resolução é reduzida apenas para o segundo
hasen
bem para ser justo, as unidades são microssegundos (CLOCKS_PER_SEC é 1000000 em todos os sistemas POSIX). Só tem resolução de segundos. :-P.
Evan Teran
1

Isso deve funcionar ... testado em um mac ...

#include <stdio.h>
#include <sys/time.h>

int main() {
        struct timeval tv;
        struct timezone tz;
        struct tm *tm;
        gettimeofday(&tv,&tz);
        tm=localtime(&tv.tv_sec);
        printf("StartTime: %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec);
}

Sim ... execute duas vezes e subtraia ...

Jason Punyon
fonte
1

No padrão POSIX clock, seu valor de retorno é definido em termos do símbolo CLOCKS_PER_SEC e uma implementação é livre para definir isso de qualquer maneira conveniente. No Linux, tive sorte com a times()função.

Jon Trauntvein
fonte
1

gettimeofday - o problema é que pode ter valores menores se você alterar o clock do hardware (com NTP por exemplo) Boost - não disponível para este projeto clock () - normalmente retorna um inteiro de 4 bytes, o que significa que é uma baixa capacidade, e depois de algum tempo, ele retorna números negativos.

Prefiro criar minha própria aula e atualizar a cada 10 milissegundos, então dessa forma é mais flexível, podendo até melhorar para ter inscritos.

class MyAlarm {
static int64_t tiempo;
static bool running;
public:
static int64_t getTime() {return tiempo;};
static void callback( int sig){
    if(running){
        tiempo+=10L;
    }
}
static void run(){ running = true;}
};

int64_t MyAlarm::tiempo = 0L;
bool MyAlarm::running = false;

para atualizá-lo, eu uso setitimer:

int main(){
struct sigaction sa; 
struct itimerval timer; 

MyAlarm::run();
memset (&sa, 0, sizeof (sa)); 
sa.sa_handler = &MyAlarm::callback; 

sigaction (SIGALRM, &sa, NULL); 


timer.it_value.tv_sec = 0; 
timer.it_value.tv_usec = 10000; 



timer.it_interval.tv_sec = 0; 
timer.it_interval.tv_usec = 10000; 


setitimer (ITIMER_REAL, &timer, NULL); 
.....

Veja setitimer e ITIMER_VIRTUAL e ITIMER_REAL.

Não use as funções de alarme ou ualarm, você terá baixa precisão quando seu processo for difícil.

Hola Soy Edu Feliz Navidad
fonte
0

Eu prefiro a biblioteca Boost Timer por sua simplicidade, mas se você não quiser usar bibliotecas de terceiros, usar clock () parece razoável.

Nick Presta
fonte
0

Como uma atualização, parece que no Windows Clock () mede o tempo do relógio de parede (com precisão CLOCKS_PER_SEC)

 http://msdn.microsoft.com/en-us/library/4e2ess30(VS.71).aspx

enquanto no Linux ele mede o tempo de CPU nos núcleos usados ​​pelo processo atual

http://www.manpagez.com/man/3/clock

e (parece, e conforme observado pelo autor original), na verdade, com menos precisão do que CLOCKS_PER_SEC, embora talvez isso dependa da versão específica do Linux.

rogerdpack
fonte
0

Eu gosto do método Hola Soy de não usar gettimeofday (). Aconteceu comigo em um servidor em execução, o administrador mudou o fuso horário. O relógio foi atualizado para mostrar o mesmo valor local (correto). Isso fez com que as funções time () e gettimeofday () mudassem 2 horas e todos os carimbos de data / hora em alguns serviços travassem.

gkolarov
fonte
0

Eu escrevi uma C++aula usando timeb.

#include <sys/timeb.h>
class msTimer 
{
public:
    msTimer();
    void restart();
    float elapsedMs();
private:
    timeb t_start;
};

Funções de membro:

msTimer::msTimer() 
{ 
    restart(); 
}

void msTimer::restart() 
{ 
    ftime(&t_start); 
}

float msTimer::elapsedMs() 
{
    timeb t_now;
    ftime(&t_now);
    return (float)(t_now.time - t_start.time) * 1000.0f +
           (float)(t_now.millitm - t_start.millitm);
}

Exemplo de uso:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char** argv) 
{
    msTimer t;
    for (int i = 0; i < 5000000; i++)
        ;
    std::cout << t.elapsedMs() << endl;
    return 0;
}

A saída do meu computador é '19'. A precisão da msTimerclasse é da ordem de milissegundos. No exemplo de uso acima, o tempo total de execução usado pelo for-loop é rastreado. Desta vez, incluiu a alternância do sistema operacional dentro e fora do contexto de execução main()devido à multitarefa.

user3674642
fonte