¿Obtener una marca de tiempo en C en microsegundos?

9 minutos de lectura

avatar de usuario
kristina arroyos

¿Cómo obtengo una marca de tiempo de microsegundos en C?

Estoy tratando de hacer:

struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_usec;

Pero esto devuelve un valor sin sentido de que si obtengo dos marcas de tiempo, la segunda puede ser más pequeña o más grande que la primera (la segunda debería siempre ser más grande). ¿Sería posible convertir el entero mágico devuelto por obtener la hora del día a un número normal con el que realmente se puede trabajar?

  • tv_usec no es “la hora actual en microsegundos” sino “la hora actual en microsegundos módulo 10^6”.

    – ruso

    29 de abril de 2011 a las 14:06

  • @ruslik ¿Cómo lo convierto en un número “normal”?

    – Kristina Brooks

    29 de abril de 2011 a las 14:08

  • @Nick Brooks: bueno… los números “normales” tienen la mala costumbre de tener rango, por lo que habrá siempre ser tal valor para el cual no hay mayor valor. Creo que deberías revisar tu algoritmo. Tratar (sec2 - sec1)*1000000 + (usec2 - usec1). O, mejor, para evitar un posible desbordamiento por segundos, si sabe que el período es menor que un segundo, cuando el segundo valor sea menor, simplemente agregue 1000000.

    – ruso

    29 de abril de 2011 a las 14:11


  • Solo necesito una marca de tiempo de microsegundos para calcular la inercia de desplazamiento. Necesito algo como time() pero por microsegundos.

    – Kristina Brooks

    29 de abril de 2011 a las 14:13

  • relacionado – stackoverflow.com/questions/361363/…

    –Nick Van Brunt

    29 de abril de 2011 a las 14:35

avatar de usuario
relajarse

También debe agregar los segundos:

unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;

Tenga en cuenta que esto solo durará alrededor de 232/106 =~ 4295 segundos, o aproximadamente 71 minutos (en un sistema típico de 32 bits).

  • Use un entero de 64 bits para almacenar microsegundos, seguramente

    – Havoc P

    29 de abril de 2011 a las 16:47

  • PROBLEMA CON SOLUCIÓN: Debido a que no comienza con tv_sec en cero, ¡time_in_micros podría reinvertirse en CUALQUIER momento! No hay necesidad de 64 bits de tiempo, ya sea como un solo uint64_t o como dos valores de 32 bits. Como mínimo, cuando inicie el cronometraje, guarde el valor tv_sec de la hora de inicio y réstelo de todos los valores de cronometraje futuros.

    –Tom Oeste

    19 de julio de 2012 a las 20:32


  • Dejar que el valor cambie en un sistema de 32 bits no es tan malo si sabe lo que está haciendo. Todavía puede obtener valores sensatos del cálculo de current_microseconds-start_time si el intervalo nunca supera los 71 minutos en su implementación.

    – Zouppen

    12 de marzo de 2013 a las 9:33

avatar de usuario
Robᵩ

Tiene dos opciones para obtener una marca de tiempo de microsegundos. La primera (y mejor) elección es utilizar el timeval escriba directamente:

struct timeval GetTimeStamp() {
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return tv;
}

La segunda opción, y para mí menos deseable, es construir un uint64_t a partir de un timeval:

uint64_t GetTimeStamp() {
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return tv.tv_sec*(uint64_t)1000000+tv.tv_usec;
}

  • por qué (uint64_t)1000000 en lugar de 1000000ull?

    – Pato mugido

    28 de julio de 2014 a las 16:56

  • @Mooing Duck, para mí (un programador menos experimentado que tú), (uint64_t)1000000 en realidad es más legible que 1000000ull. Ahora que los he visto juntos, el último tiene mucho sentido, pero habría reconocido al primero incluso como un codificador mucho más joven, mientras que el último habría requerido que lo buscara para verificar lo que significa.

    – Gabriel grapas

    30 de agosto de 2015 a las 2:49


  • ull no especifica exactamente el mismo número de bits en todos los sistemas. uintXX_t es una buena seguridad de que obtendrá exactamente lo que desea.

    – kylefinn

    19 de junio de 2017 a las 22:26

struct timeval contiene dos componentes, el segundo y el microsegundo. Una marca de tiempo con precisión de microsegundos se representa como segundos desde la época almacenada en el campo tv_sec y las fracciones de microsegundos en tv_usec. Por lo tanto, no puede simplemente ignorar tv_sec y esperar resultados sensatos.

Si usa Linux o *BSD, puede usar timersub() para restar dos valores de struct timeval, que podrían ser lo que desea.

avatar de usuario
Ciro Santilli Путлер Капут 六四事

timespec_get de C11

Devuelve con precisión de hasta nanosegundos, redondeado a la resolución de la implementación.

#include <time.h>
struct timespec ts;
timespec_get(&ts, TIME_UTC);
struct timespec {
    time_t   tv_sec;        /* seconds */
    long     tv_nsec;       /* nanoseconds */
};

Más detalles aquí: https://stackoverflow.com/a/36095407/895245

avatar de usuario
grapas gabriel

Cómo obtener una marca de tiempo simple en C

  1. en milisegundos (ms) con función millis(),
  2. microsegundos (nosotros) con micros()y
  3. nanosegundos (ns) con nanos()

Esto funciona en C11. Ver documentación para timespec_get() aquí.

También funciona en C++17. Ver documentación para std::timespec_get() aquí.

Sin embargo, para C++ 11 y versiones posteriores, prefiero usar un enfoque diferente donde puedo especifica la resolucion y escriba el reloj en su lugar, como demuestro en mi respuesta aquí: Obtener un tiempo de ejecución preciso en C++ (microsegundos).

El C11 timespec_get() La solución, por otro lado, es un poco más limitada ya que no puede especificar la resolución del reloj ni la monotonicidad (un reloj “monotónico” se define como un reloj que solo cuenta hacia adelante y puede Nunca ir o saltar hacia atrás, por ejemplo, para correcciones de tiempo). Al medir las diferencias de tiempo, se desean relojes monótonos para asegurarse de que nunca cuente un salto de corrección de reloj como parte de su tiempo “medido”.

La resolución de los valores de marca de tiempo devueltos por las siguientes funciones, por lo tanto, dado que no podemos especificar el reloj a usar, puede depender de su arquitectura de hardware, Sistema operativoy compilador.

De todos modos, Aquí estan mis millis(), micros()y nanos() Funciones que uso en C11 para marcas de tiempo simples y creación de perfiles de velocidad de código:

#include <time.h>

/// Convert seconds to milliseconds
#define SEC_TO_MS(sec) ((sec)*1000)
/// Convert seconds to microseconds
#define SEC_TO_US(sec) ((sec)*1000000)
/// Convert seconds to nanoseconds
#define SEC_TO_NS(sec) ((sec)*1000000000)

/// Convert nanoseconds to seconds
#define NS_TO_SEC(ns)   ((ns)/1000000000)
/// Convert nanoseconds to milliseconds
#define NS_TO_MS(ns)    ((ns)/1000000)
/// Convert nanoseconds to microseconds
#define NS_TO_US(ns)    ((ns)/1000)

/// Get a time stamp in milliseconds.
uint64_t millis()
{
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);
    uint64_t ms = SEC_TO_MS((uint64_t)ts.tv_sec) + NS_TO_MS((uint64_t)ts.tv_nsec);
    return ms;
}

/// Get a time stamp in microseconds.
uint64_t micros()
{
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);
    uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec);
    return us;
}

/// Get a time stamp in nanoseconds.
uint64_t nanos()
{
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);
    uint64_t ns = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
    return ns;
}

// NB: for all 3 timestamp functions above: gcc defines the type of the internal
// `tv_sec` seconds value inside the `struct timespec`, which is used
// internally in these functions, as a signed `long int`. For architectures
// where `long int` is 64 bits, that means it will have undefined
// (signed) overflow in 2^64 sec = 5.8455 x 10^11 years. For architectures
// where this type is 32 bits, it will occur in 2^32 sec = 136 years. If the
// implementation-defined epoch for the timespec is 1970, then your program
// could have undefined behavior signed time rollover in as little as
// 136 years - (year 2021 - year 1970) = 136 - 51 = 85 years. If the epoch
// was 1900 then it could be as short as 136 - (2021 - 1900) = 136 - 121 =
// 15 years. Hopefully your program won't need to run that long. :). To see,
// by inspection, what your system's epoch is, simply print out a timestamp and
// calculate how far back a timestamp of 0 would have occurred. Ex: convert
// the timestamp to years and subtract that number of years from the present
// year.

Referencias:

  1. Las fuentes de documentación de cppreference.com a las que vinculo arriba.
  2. Esta respuesta aquí por @Ciro Santilli新疆棉花

Ver también:

  1. Mis 3 conjuntos de funciones de marca de tiempo (entrecruzadas entre sí):
    1. Para Marcas de tiempo Cvea mi respuesta aquí: ¿Obtener una marca de tiempo en C en microsegundos?
    2. Para Marcas de tiempo de alta resolución de C++vea mi respuesta aquí: Obtener un tiempo de ejecución preciso en C++ (microsegundos)
    3. Para Marcas de tiempo de alta resolución de Pythonvea mi respuesta aquí: ¿Cómo puedo obtener marcas de tiempo de resolución de milisegundos y microsegundos en Python?
  2. https://en.cppreference.com/w/c/crono/reloj
    1. POSIX clock_gettime(): https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
  3. clock_gettime() en Linux: https://linux.die.net/man/3/clock_gettime
    1. Nota: para C11 y posteriores, usted lata utilizar timespec_get()como he hecho anteriormente, en lugar de POSIX clock_gettime(). https://en.cppreference.com/w/c/crono/reloj dice:

      usa timespec_get en C11

    2. Pero, usando clock_gettime() en su lugar, le permite elegir una ID de reloj deseada para el tipo de reloj ¡usted quiere! Ver también aquí: ***** https://people.cs.rutgers.edu/~pxk/416/notes/c-tutorials/gettime.html

Que hacer:

  1. Ya que timespec_getres() no es compatible hasta C23, actualice mis ejemplos para incluir uno que use POSIX clock_gettime() y clock_getres() funciones en Linux. Me gustaría saber con precisión qué tan buena es la resolución del reloj que puedo esperar en un sistema dado. ¿Es ms-resolución, us-resolución, ns-resolución, algo más? Para referencia, consulte:
    1. https://linux.die.net/man/3/clock_gettime
    2. https://people.cs.rutgers.edu/~pxk/416/notes/c-tutorials/gettime.html
    3. https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

Pero esto devuelve un valor sin sentido de que si obtengo dos marcas de tiempo, la segunda puede ser más pequeña o más grande que la primera (la segunda siempre debe ser más grande).

¿Qué te hace pensar que? El valor probablemente esté bien. Es la misma situación que con los segundos y los minutos: cuando mides el tiempo en minutos y segundos, la cantidad de segundos se reduce a cero cuando llega a sesenta.

Para convertir el valor devuelto en un número “lineal”, puede multiplicar la cantidad de segundos y agregar los microsegundos. Pero si cuento correctamente, un año es aproximadamente 1e6*60*60*24*360 μseg y eso significa que necesitará más de 32 bits para almacenar el resultado:

$ perl -E '$_=1e6*60*60*24*360; say int log($_)/log(2)'
44

Esa es probablemente una de las razones para dividir el valor devuelto original en dos partes.

avatar de usuario
Mathieu rodic

use un long long sin signo (es decir, una unidad de 64 bits) para representar la hora del sistema:

typedef unsigned long long u64;

u64 u64useconds;
struct timeval tv;

gettimeofday(&tv,NULL);
u64useconds = (1000000*tv.tv_sec) + tv.tv_usec;

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad