C ++ obtiene milisegundos de tiempo en Linux: el reloj () no parece funcionar correctamente

102

En Windows, clock()devuelve el tiempo en milisegundos, pero en esta caja de Linux en la que estoy trabajando, lo redondea al 1000 más cercano, por lo que la precisión es solo al "segundo" nivel y no al nivel de milisegundos.

Encontré una solución con Qt usando la QTimeclase, instanciando un objeto y llamándolo y start()luego llamando elapsed()para obtener la cantidad de milisegundos transcurridos.

Tuve suerte porque estoy trabajando con Qt para empezar, pero me gustaría una solución que no dependa de bibliotecas de terceros,

¿No hay una forma estándar de hacer esto?

ACTUALIZAR

No recomiende Boost ..

Si Boost y Qt pueden hacerlo, seguramente no es magia, ¡debe haber algo estándar que estén usando!

hasen
fuente
2
Acerca de la edición, pero hacerlo de forma portátil es algo complicado.
Anónimo

Respuestas:

37

Puede usar gettimeofday al principio y al final de su método y luego diferenciar las dos estructuras de retorno. Obtendrá una estructura como la siguiente:

struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
}

EDITAR: Como sugieren los dos comentarios a continuación, clock_gettime (CLOCK_MONOTONIC) es una opción mucho mejor si lo tiene disponible, que debería estar en casi todas partes en estos días.

EDITAR: Alguien más comentó que también puede usar C ++ moderno con std :: chrono :: high_resolution_clock, pero no se garantiza que sea monótono. En su lugar, utilice reloj_constante.

Adam Hawes
fuente
38
terrible para un trabajo serio. Grandes problemas dos veces al año, cuando alguien hace citas y, por supuesto, la sincronización NTP. Use clock_gettime (CLOCK_MONOTONIC,)
AndrewStone
9
@AndrewStone: La hora de UNIX no cambia dos veces al año. O incluso una vez al año. Pero, sí, CLOCK_MONOTONICes excelente para evitar ajustes de hora localizados en el sistema.
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
fuente
4
¿Por qué agrega +0.5 a la diferencia?
Mahmoud Al-Qudsi
19
@Computer Guru, es una técnica común para redondear valores positivos. Cuando el valor se trunca a un valor entero, cualquier valor entre 0.0 y 0.4999 ... antes de que la adición se trunque a 0, y entre 0.5 y 0.9999 ... se trunca a 1.
Mark Ransom
8
tv_usec no es milisegundos, es microcsegundos.
NebulaFox
13
terrible para un trabajo serio. Grandes problemas dos veces al año, cuando alguien tiene citas y, por supuesto, sincronización NTP
AndrewStone
14
@AndrewStone tiene razón, use clock_gettime (2) con CLOCK_REALTIME para comparar tiempos en la misma computadora. De la página de POSIX.1-2008 marks gettimeofday() as obsolete, recommending the use of clock_gettime(2) instead. manual de gettimeofday (2): @CTT, ¿podría actualizar el ejemplo cambiando struct timevala struct timespec, y gettimeofday(&start, NULL)a clock_gettime(CLOCK_MONOTONIC, &start)para que la gente no tenga problemas?
Bobby Powers
58

Tenga en cuenta que clockqué no medir el tiempo de reloj de pared. Eso significa que si su programa tarda 5 segundos, clockno medirá necesariamente 5 segundos, pero podría más (su programa podría ejecutar varios subprocesos y, por lo tanto, podría consumir más CPU que el tiempo real) o menos. Mide una aproximación del tiempo de CPU utilizado. Para ver la diferencia, 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;
}

Sale en mi sistema

$ difference: 0

¡Porque todo lo que hicimos fue dormir y no usar tiempo de CPU! Sin embargo, usando gettimeofdayobtenemos lo 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;
}

Salidas en mi sistema

$ difference: 5

Si necesita más precisión pero desea obtener el tiempo de la CPU , entonces puede considerar usar la getrusagefunción.

Johannes Schaub - litb
fuente
⁺¹ sobre mencionar a sleep()- ya pensé en hacer una pregunta (¿por qué funciona bien para todos excepto para mí?) , Cuando encontré su respuesta.
Hi-Angel
18

También recomiendo las herramientas que ofrece Boost. O el mencionado Boost Timer, o hackear algo de Boost.DateTime o hay una nueva biblioteca propuesta en la caja de arena: Boost.Chrono : esta última será un reemplazo para el Timer y contará con:

  • Las utilidades de tiempo de la biblioteca estándar C ++ 0x, que incluyen:
    • Plantilla de clase duration
    • Plantilla de clase time_point
    • Relojes:
      • system_clock
      • monotonic_clock
      • high_resolution_clock
  • Plantilla de clase timer, con typedefs:
    • system_timer
    • monotonic_timer
    • high_resolution_timer
  • Procesar relojes y temporizadores:
    • process_clock, capturando tiempos reales, de CPU de usuario y de CPU del sistema.
    • process_timer, capturando tiempos reales transcurridos, CPU de usuario y CPU del sistema.
    • run_timer, informe conveniente de | process_timer | resultados.
  • La aritmética racional en tiempo de compilación de la biblioteca estándar C ++ 0x.

Aquí está la fuente de la lista de funciones

Anónimo
fuente
Por ahora, puede usar el temporizador de impulso y luego migrar con gracia a Chrono cuando se revise / acepte.
Anónimo
13

Escribí una Timerclase basada en la respuesta de CTT . Se puede utilizar de la siguiente manera:

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

Aquí está su implementación:

#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
fuente
2
Esto es genial, pero los "nseconds" son engañosos porque timeval no contiene nanosegundos, contiene microsegundos, por lo que sugeriría que la gente lo llame "useconds".
pho0
Gracias. Corrección realizada.
Chris Redford
9

Si no necesita que el código sea portátil a unices antiguos, puede usar clock_gettime (), que le dará el tiempo en nanosegundos (si su procesador admite esa resolución). Es POSIX, pero de 2001.

Zachary Hamm
fuente
4

clock () tiene una resolución a menudo bastante pésima. Si desea medir el tiempo en el nivel de milisegundos, una alternativa es usar clock_gettime (), como se explica en esta pregunta.

(Recuerde que debe vincularse con -lrt en Linux).

JesperE
fuente
4

Con C ++ 11 y std::chrono::high_resolution_clockpuedes hacer esto:

#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;
}

Salida:

Delta t2-t1: 3 milliseconds

Enlace a la demostración: http://cpp.sh/2zdtu

Martín G
fuente
2

clock () no devuelve milisegundos o segundos en linux. Por lo general, clock () devuelve microsegundos en un sistema Linux. La forma correcta de interpretar el valor devuelto por clock () es dividirlo por CLOCKS_PER_SEC para averiguar cuánto tiempo ha pasado.

Plataforma improvisada
fuente
no en la caja en la que estoy trabajando! además, estoy dividiendo por CLOCKS_PER_SEC, pero no tiene sentido porque la resolución es solo hasta el segundo
hasen
Bueno, para ser justos, las unidades son microsegundos (CLOCKS_PER_SEC es 1000000 en todos los sistemas POSIX). Solo tiene una resolución de segundos. :-PAGS.
Evan Teran
1

Esto debería funcionar ... probado en una 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);
}

Sí ... ejecútelo dos veces y reste ...

Jason Punyon
fuente
1

En el estándar POSIX clocktiene su valor de retorno definido en términos del símbolo CLOCKS_PER_SEC y una implementación es libre de definir esto de cualquier manera conveniente. En Linux, he tenido buena suerte con la times()función.

Jon Trauntvein
fuente
1

gettimeofday - el problema es que puede tener valores más bajos si cambia su reloj de hardware (con NTP por ejemplo) Boost - no disponible para este proyecto clock () - generalmente devuelve un entero de 4 bytes, lo que significa que es de baja capacidad, y después de algún tiempo devuelve números negativos.

Prefiero crear mi propia clase y actualizar cada 10 milisegundos, así es más flexible e incluso puedo mejorarla para tener suscriptores.

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 actualizarlo 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); 
.....

Mira setitimer y ITIMER_VIRTUAL e ITIMER_REAL.

No utilice las funciones de alarma o ualarm, tendrá poca precisión cuando su proceso se convierta en un trabajo duro.

Hola Soy Edu Feliz Navidad
fuente
0

Prefiero la biblioteca Boost Timer por su simplicidad, pero si no desea usar bibliotecas de terceros, usar clock () parece razonable.

Nick Presta
fuente
0

Como actualización, parece que en Windows clock () mide el tiempo del reloj de pared (con precisión CLOCKS_PER_SEC)

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

mientras que en Linux, mide el tiempo de la CPU en los núcleos utilizados por el proceso actual

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

y (parece, y como lo indica el póster original) en realidad con menos precisión que CLOCKS_PER_SEC, aunque tal vez esto dependa de la versión específica de Linux.

Rogerdpack
fuente
0

Me gusta el método Hola Soy de no usar gettimeofday (). Me pasó en un servidor en ejecución que el administrador cambió la zona horaria. El reloj se actualizó para mostrar el mismo valor local (correcto). Esto provocó que la función time () y gettimeofday () cambiaran 2 horas y todas las marcas de tiempo en algunos servicios se bloquearon.

gkolarov
fuente
0

Escribí una C++clase usando timeb.

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

Funciones de los miembros:

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);
}

Ejemplo 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;
}

La salida en mi computadora es '19'. La precisión de la msTimerclase es del orden de milisegundos. En el ejemplo de uso anterior, forse realiza un seguimiento del tiempo total de ejecución ocupado por el bucle. Esta vez incluyó el cambio del sistema operativo dentro y fuera del contexto de ejecución main()debido a la multitarea.

usuario3674642
fuente