Usando apenas ANSI C, existe alguma maneira de medir o tempo com precisão de milissegundos ou mais? Eu estava navegando no time.h, mas só encontrei segundas funções de precisão.
Usando apenas ANSI C, existe alguma maneira de medir o tempo com precisão de milissegundos ou mais? Eu estava navegando no time.h, mas só encontrei segundas funções de precisão.
Respostas:
Não há nenhuma função ANSI C que ofereça resolução melhor que 1 segundo, mas a função POSIX gettimeofday
fornece resolução de microssegundos. A função de relógio mede apenas a quantidade de tempo que um processo passou executando e não é precisa em muitos sistemas.
Você pode usar esta função assim:
struct timeval tval_before, tval_after, tval_result;
gettimeofday(&tval_before, NULL);
// Some code you want to time, for example:
sleep(1);
gettimeofday(&tval_after, NULL);
timersub(&tval_after, &tval_before, &tval_result);
printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);
Isso retorna Time elapsed: 1.000870
na minha máquina.
timeval::tv_usec
está sempre abaixo de um segundo, está em loop. Ou seja, para ter diferenças de tempo maiores que 1 segundo, você deve:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
incorporada na função. Podemos usar tval_result
valores (tv_sec e tv_usec) como estão.
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
pode ser inexata, o que poderia afetar o resultado final (embora, na minha experiência CLOCKS_PER_SEC
, sempre tenha sido um múltiplo de 1000). Fazer (1000 * clock()) / CLOCKS_PER_SEC
é menos suscetível à inexatidão de divisão, mas, por outro lado, é mais suscetível ao transbordamento. Apenas algumas questões a considerar.
Eu sempre uso a função clock_gettime (), retornando a hora do relógio CLOCK_MONOTONIC. O tempo retornado é a quantidade de tempo, em segundos e nanossegundos, desde algum ponto não especificado no passado, como a inicialização do sistema na época.
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
int main(int argc, char **argv)
{
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// Some code I am interested in measuring
clock_gettime(CLOCK_MONOTONIC, &end);
uint64_t timeElapsed = timespecDiff(&end, &start);
}
clock_gettime(CLOCK_MONOTONIC, ...)
e há até a macro de teste de recursos _POSIX_MONOTONIC_CLOCK
.
Implementando uma solução portátil
Como já foi mencionado aqui que não existe uma solução ANSI adequada com precisão suficiente para o problema de medição de tempo, quero escrever sobre as maneiras de obter uma solução de medição de tempo portátil e, se possível, de alta resolução.
Relógio monotônico x time stamp
De um modo geral, existem duas maneiras de medir o tempo:
O primeiro usa um contador de relógio monotônico (às vezes chamado de contador de ticks), que conta os ticks com uma frequência predefinida; portanto, se você tiver um valor de ticks e a frequência for conhecida, poderá converter facilmente os ticks em tempo decorrido. Na verdade, não é garantido que um relógio monotônico reflita a hora atual do sistema de qualquer forma; ele também pode contar tiques desde a inicialização do sistema. Mas garante que um relógio sempre funcione de maneira crescente, independentemente do estado do sistema. Normalmente, a frequência está ligada a uma fonte de alta resolução de hardware, por isso fornece alta precisão (depende do hardware, mas a maioria do hardware moderno não tem problemas com fontes de relógio de alta resolução).
A segunda maneira fornece um valor de hora (data) com base no valor atual do relógio do sistema. Ele também pode ter uma alta resolução, mas tem uma grande desvantagem: esse tipo de valor de hora pode ser afetado por diferentes ajustes de hora do sistema, ou seja, alteração de fuso horário, alteração de horário de verão (DST), atualização do servidor NTP, hibernação do sistema etc. em. Em algumas circunstâncias, você pode obter um valor de tempo decorrido negativo que pode levar a um comportamento indefinido. Na verdade, esse tipo de fonte de tempo é menos confiável que a primeira.
Portanto, a primeira regra na medição do intervalo de tempo é usar um relógio monotônico, se possível. Geralmente, possui alta precisão e é confiável por design.
Estratégia de fallback
Ao implementar uma solução portátil, vale a pena considerar uma estratégia de fallback: use um relógio monotônico, se disponível, e o fallback para registro de data e hora se aproximar, se não houver um relógio monotônico no sistema.
janelas
Existe um ótimo artigo chamado Adquirindo carimbos de data / hora de alta resolução no MSDN sobre medição de tempo no Windows, que descreve todos os detalhes que você precisa saber sobre o suporte de software e hardware. Para adquirir um carimbo de data / hora de alta precisão no Windows, você deve:
consulte uma frequência do timer (ticks por segundo) com QueryPerformanceFrequency :
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
A frequência do timer é fixada na inicialização do sistema, portanto, você precisa obtê-la apenas uma vez.
consulte o valor atual dos ticks com QueryPerformanceCounter :
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
dimensione os ticks para o tempo decorrido, ou seja, para microssegundos:
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
De acordo com a Microsoft, você não deve ter problemas com essa abordagem no Windows XP e versões posteriores na maioria dos casos. Mas você também pode usar duas soluções de fallback no Windows:
GetTickCount
, mas está disponível a partir do Windows Vista e superior.OS X (macOS)
O OS X (macOS) possui suas próprias unidades de tempo absoluto, que representam um relógio monotônico. A melhor maneira de começar é o artigo da Apple, Perguntas e respostas técnicas QA1398: Unidades de tempo absoluto do Mach, que descreve (com os exemplos de código) como usar a API específica do Mach para obter ticks monotônicos. Também existe uma pergunta local sobre ela chamada alternativa clock_gettime no Mac OS X que, no final, pode deixar você um pouco confuso sobre o que fazer com o possível excesso de valor, porque a frequência do contador é usada na forma de numerador e denominador. Então, um pequeno exemplo de como obter o tempo decorrido:
obtenha o numerador e o denominador da frequência do relógio:
#include <mach/mach_time.h>
#include <stdint.h>
static uint64_t freq_num = 0;
static uint64_t freq_denom = 0;
void init_clock_frequency ()
{
mach_timebase_info_data_t tb;
if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) {
freq_num = (uint64_t) tb.numer;
freq_denom = (uint64_t) tb.denom;
}
}
Você precisa fazer isso apenas uma vez.
consulte o valor atual do tick com mach_absolute_time
:
uint64_t tick_value = mach_absolute_time ();
dimensione os ticks para o tempo decorrido, ou seja, para microssegundos, usando numerador e denominador consultados anteriormente:
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
A principal idéia para evitar um transbordamento é diminuir a escala dos ticks com a precisão desejada antes de usar o numerador e o denominador. Como a resolução inicial do timer é em nanossegundos, nós a dividimos 1000
para obter microssegundos. Você pode encontrar a mesma abordagem usada no time_mac.c do Chromium . Se você realmente precisa de uma precisão de nanossegundos, considere a leitura de Como posso usar o mach_absolute_time sem transbordar? .
Linux e UNIX
A clock_gettime
chamada é o seu melhor caminho em qualquer sistema compatível com POSIX. Ele pode consultar a hora de diferentes fontes de relógio, e a que precisamos é CLOCK_MONOTONIC
. Nem todos os sistemas com clock_gettime
suporte CLOCK_MONOTONIC
, portanto, a primeira coisa que você precisa fazer é verificar sua disponibilidade:
_POSIX_MONOTONIC_CLOCK
for definido com um valor >= 0
, significa que CLOCK_MONOTONIC
está disponível;se _POSIX_MONOTONIC_CLOCK
estiver definido 0
, significa que você deve verificar adicionalmente se funciona em tempo de execução, sugiro usar sysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
O uso de clock_gettime
é bastante simples:
obtenha o valor do tempo:
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_posix_clock_time ()
{
struct timespec ts;
if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000);
else
return 0;
}
Reduzi o tempo para microssegundos aqui.
calcule a diferença com o valor de tempo anterior recebido da mesma maneira:
uint64_t prev_time_value, time_value;
uint64_t time_diff;
/* Initial time */
prev_time_value = get_posix_clock_time ();
/* Do some work here */
/* Final time */
time_value = get_posix_clock_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
A melhor estratégia de fallback é usar a gettimeofday
chamada: não é monotônica, mas fornece uma resolução bastante boa. A idéia é a mesma que com clock_gettime
, mas para obter um valor de tempo, você deve:
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_gtod_clock_time ()
{
struct timeval tv;
if (gettimeofday (&tv, NULL) == 0)
return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
else
return 0;
}
Novamente, o valor do tempo é reduzido para microssegundos.
SGI IRIX
O IRIX tem a clock_gettime
ligação, mas falta CLOCK_MONOTONIC
. Em vez disso, possui sua própria fonte de relógio monotônico definida como a CLOCK_SGI_CYCLE
qual você deve usar em vez de CLOCK_MONOTONIC
com clock_gettime
.
Solaris e HP-UX
O Solaris possui sua própria interface de timer de alta resolução, gethrtime
que retorna o valor atual do timer em nanossegundos. Embora as versões mais recentes do Solaris possam ter clock_gettime
, você pode seguirgethrtime
se precisar dar suporte a versões antigas do Solaris.
O uso é simples:
#include <sys/time.h>
void time_measure_example ()
{
hrtime_t prev_time_value, time_value;
hrtime_t time_diff;
/* Initial time */
prev_time_value = gethrtime ();
/* Do some work here */
/* Final time */
time_value = gethrtime ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
O HP-UX não possui clock_gettime
, mas suporta os gethrtime
quais você deve usar da mesma maneira que no Solaris.
BeOS
O BeOS também possui sua própria interface de timer de alta resolução, system_time
que retorna o número de microssegundos decorridos desde que o computador foi inicializado.
Exemplo de uso:
#include <kernel/OS.h>
void time_measure_example ()
{
bigtime_t prev_time_value, time_value;
bigtime_t time_diff;
/* Initial time */
prev_time_value = system_time ();
/* Do some work here */
/* Final time */
time_value = system_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
OS / 2
O OS / 2 possui sua própria API para recuperar registros de data e hora de alta precisão:
consulte uma frequência do timer (ticks por unidade) com DosTmrQueryFreq
(para compilador GCC):
#define INCL_DOSPROFILE
#define INCL_DOSERRORS
#include <os2.h>
#include <stdint.h>
ULONG freq;
DosTmrQueryFreq (&freq);
consulte o valor atual dos ticks com DosTmrQueryTime
:
QWORD tcounter;
unit64_t time_low;
unit64_t time_high;
unit64_t timestamp;
if (DosTmrQueryTime (&tcounter) == NO_ERROR) {
time_low = (unit64_t) tcounter.ulLo;
time_high = (unit64_t) tcounter.ulHi;
timestamp = (time_high << 32) | time_low;
}
dimensione os ticks para o tempo decorrido, ou seja, para microssegundos:
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
Implementação de exemplo
Você pode dar uma olhada na biblioteca plibsys , que implementa todas as estratégias descritas acima (consulte ptimeprofiler * .c para obter detalhes).
timespec_get
: stackoverflow.com/a/36095407/895245
timespec_get
não é monotônico.
timespec_get
de C11
Retorna em nanossegundos, arredondado para a resolução da implementação.
Parece um ripoff ANSI do POSIX ' clock_gettime
.
Exemplo: a printf
é feito a cada 100ms no Ubuntu 15.10:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static long get_nanos(void) {
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}
int main(void) {
long nanos;
long last_nanos;
long start;
nanos = get_nanos();
last_nanos = nanos;
start = nanos;
while (1) {
nanos = get_nanos();
if (nanos - last_nanos > 100000000L) {
printf("current nanos: %ld\n", nanos - start);
last_nanos = nanos;
}
}
return EXIT_SUCCESS;
}
O rascunho padrão do C11 N1570 7.27.2.5 "A função timespec_get diz":
Se base for TIME_UTC, o membro tv_sec será definido como o número de segundos desde que uma época definida pela implementação será truncada para um valor inteiro e o membro tv_nsec será definido como o número inteiro de nanossegundos, arredondado para a resolução do relógio do sistema. (321)
321) Embora um objeto struct timespec descreva tempos com resolução em nanossegundos, a resolução disponível depende do sistema e pode até ser maior que 1 segundo.
O C ++ 11 também recebeu std::chrono::high_resolution_clock
: Temporizador de alta resolução entre plataformas C ++
implementação glibc 2.21
Pode ser encontrado em sysdeps/posix/timespec_get.c
:
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
case TIME_UTC:
if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
return 0;
break;
default:
return 0;
}
return base;
}
tão claramente:
única TIME_UTC
é atualmente suportada
encaminha para __clock_gettime (CLOCK_REALTIME, ts)
, que é uma API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
O Linux x86-64 possui uma clock_gettime
chamada de sistema.
Observe que este não é um método de micro-benchmarking à prova de falhas porque:
man clock_gettime
diz que essa medida pode ter descontinuidades se você alterar alguma configuração de hora do sistema enquanto o programa é executado. É claro que esse deve ser um evento raro e você poderá ignorá-lo.
isso mede o tempo da parede; portanto, se o planejador decidir esquecer sua tarefa, ele parecerá ser executado por mais tempo.
Por essas razões getrusage()
pode ser uma ferramenta melhor de avaliação comparativa POSIX, apesar da menor precisão máxima de microssegundos.
Mais informações em: Medir o tempo no Linux - hora x relógio x getrusage x clock_gettime x gettimeofday x timespec_get?
A melhor precisão possível é através do uso da instrução "rdtsc" somente no x86, que pode fornecer resolução no nível do relógio (é claro que é necessário levar em conta o custo da chamada rdtsc, que pode ser facilmente medida em inicialização do aplicativo).
O principal problema aqui é medir o número de relógios por segundo, o que não deve ser muito difícil.
A resposta aceita é boa o suficiente. Mas minha solução é mais simples. Apenas teste no Linux, use o gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.
Além disso gettimeofday
, o tv_sec
é a parte do segundo e o tv_usec
é microssegundos , não milissegundos .
long currentTimeMillis() {
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
}
int main() {
printf("%ld\n", currentTimeMillis());
// wait 1 second
sleep(1);
printf("%ld\n", currentTimeMillis());
return 0;
}
Imprimir:
1522139691342
1522139692342
exatamente um segundo.
Sob janelas:
SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);