c++ - human - ¿Por qué la marca de tiempo de microsegundos es repetitiva usando(un privado) gettimeoftheday() ie epoch
time c++ ejemplo (4)
Estoy imprimiendo microsegundos continuamente usando gettimeofday (). Como se indica en la salida del programa, puede ver que el tiempo no se actualiza en un intervalo de microsegundos sino que es repetitivo para ciertas muestras, luego aumenta en microsegundos, pero en milisegundos.
while(1)
{
gettimeofday(&capture_time, NULL);
printf(".%ld/n", capture_time.tv_usec);
}
Salida del programa:
.414719
.414719
.414719
.414719
.430344
.430344
.430344
.430344
e.t.c
Quiero que la salida se incremente secuencialmente como,
.414719
.414720
.414721
.414722
.414723
o
.414723, .414723+x, .414723+2x, .414723 +3x + ...+ .414723+nx
Parece que los microsegundos no se actualizan cuando lo adquiero de capture_time.tv_usec.
============================== // Programa completo
#include <iostream>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdio.h>
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
struct timezone
{
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
timeval capture_time; // structure
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
unsigned __int64 tmpres = 0;
static int tzflag;
if (NULL != tv)
{
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/*converting file time to unix epoch*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
tmpres /= 10; /*convert into microseconds*/
tv->tv_sec = (long)(tmpres / 1000000UL);
tv->tv_usec = (long)(tmpres % 1000000UL);
}
if (NULL != tz)
{
if (!tzflag)
{
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
int main()
{
while(1)
{
gettimeofday(&capture_time, NULL);
printf(".%ld/n", capture_time.tv_usec);// JUST PRINTING MICROSECONDS
}
}
El reloj del sistema de Windows solo funciona cada pocos milisegundos, en su caso 64 veces por segundo, de modo que cuando lo hace, aumenta el tiempo del sistema en 15.625 ms.
La solución es usar un temporizador de mayor resolución que la hora del sistema ( QueryPerformanceCounter
).
Sin embargo, todavía no verá .414723, .414723 + x, .414723 + 2x, .414723 + 3x + ... + .414723 + nx, porque su código no se ejecutará exactamente una vez cada x
microsegundos. Se ejecutará tan rápido como sea posible, pero no hay una razón en particular que siempre deba ser una velocidad constante, o que si lo es, es un número entero de microsegundos.
Esto se debe a que el proceso que ejecuta su código no siempre está programado para ejecutarse.
Mientras lo haga, se activará rápidamente, imprimiendo múltiples valores para cada microsegundo, que es un período de tiempo comparativamente largo en las CPU modernas.
Hay períodos en los que el sistema no los programa y, por lo tanto, no pueden imprimir valores.
Si lo que quiere hacer es ejecutar cada microsegundo, esto puede ser posible con algunos sistemas operativos en tiempo real que se ejecutan en hardware de alto rendimiento.
Te recomiendo que mires el encabezado C ++ 11 <chrono>
.
high_resolution_clock
(C ++ 11) el reloj con el período de tick más corto disponible
El período de marcación a la que se hace referencia aquí es la frecuencia con la que se actualiza el reloj. Si miramos en más detalles :
template< class Rep, class Period = std::ratio<1> > class duration;
La plantilla de clase
std::chrono::duration
representa un intervalo de tiempo.Consiste en un recuento de ticks de tipo Rep y un período tick , donde el tick period es una constante racional en tiempo de compilación que representa el número de segundos de una tilde a la siguiente.
Anteriormente, las funciones como gettimeofday
le daban un tiempo expresado en microsegundos, sin embargo, no podían decirle el intervalo en el que esta expresión de tiempo se actualizaba.
En el Estándar C ++ 11, esta información está ahora en claro, para hacer obvio que no hay relación entre la unidad en la que se expresa el tiempo y el período de tic . Y eso, por lo tanto, definitivamente debe tener ambos en las cuentas.
El período de marca es extremadamente importante cuando quiere medir duraciones cercanas. Si la duración que desea medir es inferior al período de marca, entonces la medirá "discretamente" como lo observó: 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, ... Aconsejo cautela en este punto.
El cambio en el tiempo que observas es de 0.414719 a 0.430344 s. La diferencia es 15.615 ms. El hecho de que la representación del número sea microsegundo no significa que se incremente en 1 microsegundo. De hecho, hubiera esperado 15.625 ms. Este es el incremento de tiempo del sistema en hardware estándar. He echado un vistazo más de cerca aquí y aquí . Esto se llama granularidad del tiempo del sistema.
Windows:
Sin embargo, hay una forma de mejorar esto, una forma de reducir la granularidad: los temporizadores multimedia . La obtención particular y la configuración de la resolución del temporizador revelarán una forma de aumentar la frecuencia de interrupción del sistema.
El código:
#define TARGET_PERIOD 1 // 1-millisecond target interrupt period
TIMECAPS tc;
UINT wTimerRes;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
// this call queries the systems timer hardware capabilities
// it returns the wPeriodMin and wPeriodMax with the TIMECAPS structure
{
// Error; application can''t continue.
}
// finding the minimum possible interrupt period:
wTimerRes = min(max(tc.wPeriodMin, TARGET_PERIOD ), tc.wPeriodMax);
// and setting the minimum period:
timeBeginPeriod(wTimerRes);
Esto obligará al sistema a funcionar a su máxima frecuencia de interrupción. Como consecuencia, la actualización del tiempo del sistema ocurrirá más a menudo y la granularidad del incremento de tiempo del sistema será close to 1 milisecond
en la mayoría de los sistemas.
Cuando merezca resolución / granularidad más allá de esto, tendrá que buscar en QueryPerformanceCounter . Pero esto se debe usar con cuidado cuando se usa durante períodos de tiempo más largos. La frecuencia de este contador se puede obtener llamando a QueryPerformanceFrequency . El SO considera esta frecuencia como una constante y dará el mismo valor todo el tiempo. Sin embargo, algunos hardware producen esta frecuencia y la verdadera frecuencia difiere del valor dado. Tiene un desplazamiento y muestra deriva térmica. Por lo tanto, el error se asumirá en el rango de varios microsegundos / segundo. Se pueden encontrar más detalles acerca de esto en el segundo enlace "aquí" de arriba.
Linux:
La situación parece algo diferente para Linux. Mira esto para tener una idea. Linux mezcla información del reloj CMOS utilizando la función getnstimeofday (por segundos desde época) e información de un contador de alta frecuencia (para microsegundos) usando la función timekeeping_get_ns . Esto no es trivial y es cuestionable en términos de precisión ya que ambas fuentes están respaldadas por hardware diferente. Las dos fuentes no están bloqueadas por fase, por lo tanto, es posible obtener más / menos de un millón de microsegundos por segundo.