c++ - subprocesos - habilitar calculo iterativo excel
El sistema de resolución de tiempo más rápido (10)
¿Cuál es el sistema de cronometraje más rápido que puede usar un programador C / C ++?
Por ejemplo:
time () dará los segundos desde el 1 de enero de 1970 a las 00:00.
GetTickCount () en Windows dará el tiempo, en milisegundos, desde el momento de inicio del sistema, pero está limitado a 49.7 días (después de eso simplemente vuelve a cero).
Quiero obtener la hora actual, o tics desde el tiempo de inicio del sistema / aplicación, en milisegundos.
La mayor preocupación es la sobrecarga del método: necesito el más ligero, porque estoy a punto de llamarlo muchas veces por segundo.
Mi caso es que tengo un hilo de trabajo, y para ese hilo de trabajo publico trabajos pendientes. Cada trabajo tiene un "tiempo de ejecución". Por lo tanto, no me importa si el tiempo es el tiempo "real" actual o el tiempo desde el tiempo de actividad del sistema; solo debe ser lineal y ligero.
Editar:
unsigned __int64 GetTickCountEx()
{
static DWORD dwWraps = 0;
static DWORD dwLast = 0;
DWORD dwCurrent = 0;
timeMutex.lock();
dwCurrent = GetTickCount();
if(dwLast > dwCurrent)
dwWraps++;
dwLast = dwCurrent;
unsigned __int64 timeResult = ((unsigned __int64)0xFFFFFFFF * dwWraps) + dwCurrent;
timeMutex.unlock();
return timeResult;
}
En Linux obtienes microsegundos:
struct timeval tv;
int res = gettimeofday(&tv, NULL);
double tmp = (double) tv.tv_sec + 1e-6 * (double) tv.tv_usec;
En Windows, solo milsegundos están disponibles:
SYSTEMTIME st;
GetSystemTime(&st);
tmp += 1e-3 * st.wMilliseconds;
return tmp;
Esto vino de R ''s datetime.c (y fue editado para abreviar).
Luego está, por supuesto, el Date_Time de Boost, que puede tener una resolución de nanosegundos en algunos sistemas (detalles aquí y aquí ).
Hace poco tuve esta pregunta e hice una investigación. La buena noticia es que los tres sistemas operativos principales proporcionan algún tipo de temporizador de alta resolución. La mala noticia es que se trata de una llamada API diferente en cada sistema. Para los sistemas operativos POSIX, usted quiere usar clock_gettime (). Sin embargo, si usa Mac OS X, esto no es compatible, debe usar mach_get_time (). Para Windows, use QueryPerformanceCounter. Alternativamente, con los compiladores que admiten OpenMP, puede usar omp_get_wtime (), pero es posible que no proporcione la resolución que está buscando.
También encontré cycle.h de fftw.org (www.fftw.org/cycle.h) para ser útil.
Aquí hay un código que llama a un temporizador en cada sistema operativo, usando algunas declaraciones #ifdef feas. El uso es muy simple: Temporizador t; t.tic (); SomeOperation (); t.toc ("Mensaje"); E imprimirá el tiempo transcurrido en segundos.
#ifndef TIMER_H
#define TIMER_H
#include <iostream>
#include <string>
#include <vector>
# if (defined(__MACH__) && defined(__APPLE__))
# define _MAC
# elif (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(_WIN64))
# define _WINDOWS
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
#endif
# if defined(_MAC)
# include <mach/mach_time.h>
# elif defined(_WINDOWS)
# include <windows.h>
# else
# include <time.h>
# endif
#if defined(_MAC)
typedef uint64_t timer_t;
typedef double timer_c;
#elif defined(_WINDOWS)
typedef LONGLONG timer_t;
typedef LARGE_INTEGER timer_c;
#else
typedef double timer_t;
typedef timespec timer_c;
#endif
//==============================================================================
// Timer
// A quick class to do benchmarking.
// Example: Timer t; t.tic(); SomeSlowOp(); t.toc("Some Message");
class Timer {
public:
Timer();
inline void tic();
inline void toc();
inline void toc(const std::string &msg);
void print(const std::string &msg);
void print();
void reset();
double getTime();
private:
timer_t start;
double duration;
timer_c ts;
double conv_factor;
double elapsed_time;
};
Timer::Timer() {
#if defined(_MAC)
mach_timebase_info_data_t info;
mach_timebase_info(&info);
conv_factor = (static_cast<double>(info.numer))/
(static_cast<double>(info.denom));
conv_factor = conv_factor*1.0e-9;
#elif defined(_WINDOWS)
timer_c freq;
QueryPerformanceFrequency(&freq);
conv_factor = 1.0/(static_cast<double>freq.QuadPart);
#else
conv_factor = 1.0;
#endif
reset();
}
inline void Timer::tic() {
#if defined(_MAC)
start = mach_absolute_time();
#elif defined(_WINDOWS)
QueryPerformanceCounter(&ts);
start = ts.QuadPart;
#else
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
start = static_cast<double>(ts.tv_sec) + 1.0e-9 *
static_cast<double>(ts.tv_nsec);
#endif
}
inline void Timer::toc() {
#if defined(_MAC)
duration = static_cast<double>(mach_absolute_time() - start);
#elif defined(_WINDOWS)
QueryPerformanceCounter(&qpc_t);
duration = static_cast<double>(qpc_t.QuadPart - start);
#else
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
duration = (static_cast<double>(ts.tv_sec) + 1.0e-9 *
static_cast<double>(ts.tv_nsec)) - start;
#endif
elapsed_time = duration*conv_factor;
}
inline void Timer::toc(const std::string &msg) { toc(); print(msg); };
void Timer::print(const std::string &msg) {
std::cout << msg << " "; print();
}
void Timer::print() {
if(elapsed_time) {
std::cout << "elapsed time: " << elapsed_time << " seconds/n";
}
}
void Timer::reset() { start = 0; duration = 0; elapsed_time = 0; }
double Timer::getTime() { return elapsed_time; }
#if defined(_WINDOWS)
# undef WIN32_LEAN_AND_MEAN
#endif
#endif // TIMER_H
Le sugiero que use la API GetSystemTimeAsFileTime si se dirige específicamente a Windows. En general es más rápido que GetSystemTime y tiene la misma precisión (que es de unos 10-15 milisegundos, no mire la resolución); cuando hice un benchmark hace algunos años bajo Windows XP, estaba en el rango de 50-100 veces más rápido.
La única desventaja es que puede tener que convertir las estructuras FILETIME devueltas a una hora de reloj utilizando, por ejemplo, FileTimeToSystemTime, si necesita acceder a las horas devueltas en un formato más amigable para los humanos. Por otro lado, siempre que no necesite esos tiempos convertidos en tiempo real, siempre puede hacerlo fuera de línea o de manera "floja" (por ejemplo, solo convierta las marcas de tiempo que necesita para mostrar / procesar, y solo cuando realmente los necesitas).
QueryPerformanceCounter puede ser una buena opción como otros han mencionado, pero la sobrecarga puede ser bastante grande dependiendo del soporte de hardware subyacente. En mi punto de referencia que menciono anteriormente, las llamadas QueryPerformanceCounter fueron 25-200 veces más lentas que las llamadas a GetSystemTimeAsFileTime. Además, hay algunos problemas de fiabilidad como, por ejemplo, se informa aquí .
Por lo tanto, en resumen: si puede hacer frente a una precisión de 10-15 milisegundos, le recomendaría que utilice GetSystemTimeAsFileTime. Si necesitas algo mejor que eso, me gustaría ir a QueryPerformanceCounter.
Pequeña exención de responsabilidad: no realicé ninguna evaluación comparativa en versiones de Windows posteriores a XP SP3. Te recomiendo que hagas algunos benchmarking por tu cuenta.
Para el tiempo, la recomendación actual de Microsoft es usar QueryPerformanceCounter
& QueryPerformanceFrequency
.
Esto le dará un tiempo mejor que milisegundo. Si el sistema no es compatible con un temporizador de alta resolución, tendrá un valor predeterminado de milisegundos (el mismo que GetTickCount
).
Aquí hay un breve artículo de Microsoft con ejemplos de por qué debería usarlo :)
Si solo te preocupa que GetTickCount()
desborde, entonces puedes envolverlo así:
DWORDLONG GetLongTickCount(void)
{
static DWORDLONG last_tick = 0;
DWORD tick = GetTickCount();
if (tick < (last_tick & 0xffffffff))
last_tick += 0x100000000;
last_tick = (last_tick & 0xffffffff00000000) | tick;
return last_tick;
}
Si desea llamar esto desde múltiples hilos, deberá bloquear el acceso a la variable last_tick
. Siempre que llame a GetLongTickCount()
al menos una vez cada 49,7 días, detectará el desbordamiento.
Si se dirige a una versión lo suficientemente tardía del sistema operativo, entonces podría usar GetTickCount64()
que tiene un punto de GetTickCount()
mucho más alto que GetTickCount()
. También podría simplemente crear una versión de GetTickCount64()
sobre GetTickCount()
.
¿Ha revisado el código en este artículo de MSDN?
http://msdn.microsoft.com/en-us/magazine/cc163996.aspx
Tengo este código compilado en una máquina con Windows 7 de 64 bits que usa tanto VC2005 como C ++ Builder XE, pero al ejecutarlo, bloquea mi máquina; no me he depurado lo suficiente como para descubrir por qué todavía. Parece demasiado complicado. Plantillas de plantillas de plantillas de UG ...
GetSystemTimeAsFileTime es el recurso más rápido. Su granularidad se puede obtener llamando a GetSystemTimeAdjustment, que llena lpTimeIncrement . La hora del sistema como tiempo de archivo tiene 100 unidades e incrementos por TimeIncrement . TimeIncrement puede variar y depende de la configuración de la interfaz del temporizador multimedia.
Una llamada a timeGetDevCaps revelará las capacidades de los servicios de tiempo. Devuelve un valor wPeriodMin para el período mínimo de interrupción compatible. Una llamada a timeBeginPeriod con wPeriodMin como argumento configurará el sistema para que funcione a la frecuencia de interrupción más alta posible (generalmente ~ 1ms). Esto también obligará a que el incremento de tiempo del archivo devuelto por GetSystemTimeAsFileTime
sea más pequeño. Su granularidad estará en el rango de 1 ms (10000 100ns unidades).
Para su propósito, sugeriría ir por este enfoque.
La elección de QueryPerformanceCounter es cuestionable ya que su frecuencia no es precisa por dos medios: en primer lugar, se desvía del valor dado por QueryPerformanceFrequency por un desplazamiento específico del hardware. Este desplazamiento puede ser fácilmente de varios cientos de ppm, lo que significa que una conversión en el tiempo contendrá un error de varios cientos de microsegundos por segundo. En segundo lugar tiene deriva térmica. La deriva de tales dispositivos puede ser fácilmente de varias ppm. De esta forma, se agrega otro - calor dependend - error de varios us / s.
Por lo tanto, siempre que una resolución de ~ 1 ms sea suficiente y la pregunta principal sea la sobrecarga, GetSystemTimeAsFileTime
es la mejor solución.
Cuando microsegundos importan, tendrías que recorrer un camino más largo y ver más detalles. Los servicios de tiempo de milisegundos se describen en el Proyecto de marca de tiempo de Windows
En Mac OS X, puede usar UInt32 TickCount (void) para obtener los tics.
POSIX admite clock_gettime () que utiliza una struct timespec
que tiene una resolución de nanosegundos. Si su sistema realmente admite una resolución precisa es más debatible, pero creo que es la llamada estándar con la resolución más alta. No todos los sistemas lo admiten, y a veces está bien oculto (library '' -lposix4
'' en Solaris, IIRC).
Actualización (2016-09-20):
- Aunque Mac OS X 10.6.4 no admitía
clock_gettime()
, y tampoco lo hizo ninguna otra versión de Mac OS X hasta e incluyendo Mac OS X 10.11.6 El Capitan, macOS Sierra 10.12 sí tiene la funciónclock_gettime()
y páginas de manual para por fin. La resolución real (enCLOCK_MONOTONIC
) todavía es microsegundos; las unidades más pequeñas son todos ceros. Esto es confirmado porclock_getres()
que informa que la resolución es 1000 nanosegundos, también conocido como 1 μs.
La página de manual de clock_gettime()
en macOS Sierra menciona mach_absolute_time()
como una forma de obtener tiempos de alta resolución. Para obtener más información, entre otros lugares, consulte las preguntas y respuestas técnicas QA1398: Unidades de tiempo absoluto de Mach y (en SO) ¿Qué es mach_absolute_time()
basado en el iPhone?