2008-10-01 10 views
12

He estado construyendo una aplicación de registro de errores recientemente y estaba buscando una manera de marcar el tiempo con precisión los datos entrantes. Cuando digo con precisión me refiero a que cada marca de tiempo debe ser precisa en relación con la otra (no es necesario sincronizarla con un reloj atómico ni nada de eso).Marca de tiempo precisa en el registro de Python

He estado usando datetime.now() como un primer intento, pero esto no es perfecta:

>>> for i in range(0,1000): 
...  datetime.datetime.now() 
... 
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 609000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 609000) 
datetime.datetime(2008, 10, 1, 13, 17, 27, 609000) 
etc. 

Los cambios entre los relojes para el primer segundo de muestras se ve así:

uSecs difference 
562000 
578000 16000 
609000 31000 
625000 16000 
640000 15000 
656000 16000 
687000 31000 
703000 16000 
718000 15000 
750000 32000 
765000 15000 
781000 16000 
796000 15000 
828000 32000 
843000 15000 
859000 16000 
890000 31000 
906000 16000 
921000 15000 
937000 16000 
968000 31000 
984000 16000 

Parece que los datos del temporizador solo se actualizan cada ~ 15-32ms en mi máquina. El problema surge cuando analizamos los datos porque ordenar por fecha distinta de la marca de tiempo y luego ordenar por marca de tiempo nuevamente puede dejar los datos en el orden incorrecto (cronológicamente). Sería bueno tener las marcas de tiempo precisas hasta el punto de que cualquier llamada al generador de marca de tiempo proporcione una marca de tiempo única.

He estado considerando algunos métodos que implican el uso de una llamada time.clock() agregada a una fecha de inicio, pero agradecería una solución que funcione con precisión a través de subprocesos en la misma máquina. Cualquier sugerencia sería muy gratamente recibida.

+0

Acabo de publicar una nueva respuesta, en * Windows * al menos, usando Python, puedes obtener marcas de tiempo de sub-microsegundo * * (NO precisión) usando el reloj Windows QPC, como demuestro en el código vinculado en mi responder. –

+0

¿Por qué demonios estás construyendo tu propio marco de trabajo? Ya hay muchos y las marcas de tiempo son un problema resuelto (hasta cierto nivel de precisión). En el caso improbable de que tenga un caso de uso que no resuelva un marco de trabajo existente, ¿puede elegir el más cercano y plantear un problema y enviar su código al mismo? – smci

+0

Porque hace ~ 8.5 años (cuando publiqué esto) las opciones eran algo más limitadas. No estaba construyendo un marco de registro de errores, estaba escribiendo algo para recibir datos UDP e información de registro de eso. Si hubiera una biblioteca disponible (y que hubiera encontrado) que hubiera hecho eso habría estado completamente abierta para usarla ;-) –

Respuesta

7

Es poco probable conseguir un control suficientemente grano fino que se puede eliminar por completo la posibilidad de marcas de tiempo duplicados - que había necesidad de una resolución más pequeña que el tiempo que lleva generar un objeto datetime. Hay un par de otros enfoques que puede tomar para tratar con él:

  1. Haz clic aquí. Deje sus marcas de tiempo no únicas tal como están, pero confíe en que el tipo de python sea estable para hacer frente a los problemas de reordenamiento. Ordenando en la marca de tiempo primero, entonces algo más retendrá el orden de la marca de tiempo - solo tienes que tener cuidado de comenzar siempre desde la lista ordenada de la marca de tiempo cada vez, en lugar de hacer múltiples ordenaciones en la misma lista.

  2. Agregue su propio valor para imponer la exclusividad. P.ej. incluir un valor entero creciente como parte de la clave, o anexar tal valor solo si las marcas de tiempo son diferentes. P.ej.

A continuación se garantizará valores timestamp únicas:

class TimeStamper(object): 
     def __init__(self): 
      self.lock = threading.Lock() 
      self.prev = None 
      self.count = 0 

     def getTimestamp(self): 
      with self.lock: 
       ts = str(datetime.now()) 
       if ts == self.prev: 
        ts +='.%04d' % self.count 
        self.count += 1 
       else: 
        self.prev = ts 
        self.count = 1 
      return ts 

para múltiples procesos (en lugar de hilos), se pone un poco más complicado sin embargo.

+1

Me doy cuenta de que esto es un poco quisquilloso, pero te refieres a "entero que aumenta estrictamente", no "entero que aumenta monótonamente". Un conjunto monótonamente creciente significa que nunca disminuye, pero podría tener valores iguales. –

+1

Todos los nitpicks aceptó con gratitud. Tienes toda la razón: he arreglado la redacción descuidada. – Brian

12

time.clock() solo mide el tiempo de reloj de pared en Windows. En otros sistemas, time.clock() realmente mide el tiempo de CPU. En esos sistemas, time.time() es más adecuado para el tiempo de reloj de pared, y tiene una resolución tan alta como Python puede manejar, que es tan alta como el sistema operativo puede administrar; usualmente usando gettimeofday (3) (resolución de microsegundos) o ftime (3) (resolución de milisegundos). Otras restricciones del sistema operativo realmente hacen que la resolución real sea mucho más alta que eso. datetime.datetime.now() usa time.time(), por lo que time.time() directamente no será mejor.

Para el registro, si uso datetime.datetime.now() en un bucle, veo aproximadamente una resolución de 1/10000 de segundo. Al mirar sus datos, tiene una resolución mucho más grosera que eso. No estoy seguro de si hay algo que Python pueda hacer, aunque es posible que pueda convencer al sistema operativo para que lo haga mejor por otros medios.

Me parece recordar que en Windows, time.clock() es en realidad (ligeramente) más preciso que time.time(), pero mide wallclock desde la primera llamada a time.clock(), por lo que tiene que recuerda "inicializarlo" primero.

+0

De hecho, esto es lo que se ve en Debian/Linux: datetime .datetime (2008, 10, 1, 17, 11, 31, 875190) datetime.datetime (2008, 10, 1, 17, 11, 31, 875199) datetime.datetime (2008, 10, 1, 17, 11 , 31, 875207) – bortzmeyer

+0

Puedo confirmar que el reloj es realmente más preciso en todas las máquinas de Windows en las que lo he probado. –

2

Aquí hay un hilo sobre exactitud de temporización de Python:

Python - time.clock() vs. time.time() - accuracy?

+0

Sí, ya había visto ese, pero esos son relativos a un proceso de inicio o la llamada al reloj en lugar de un tiempo absoluto (ish). –

3

"marca de tiempo debe ser precisa respecto a la otra"

Por qué tiempo? ¿Por qué no un número de secuencia? Si se trata de un cliente de la aplicación cliente-servidor, la latencia de red hace que las marcas de tiempo sean aleatorias.

¿Estás buscando una fuente externa de información? ¿Di un registro en otra aplicación? De nuevo, si hay una red, esos tiempos no serán demasiado cercanos.

Si debe hacer coincidir cosas entre aplicaciones separadas, considere pasar GUID para que ambas aplicaciones registren el valor GUID. Entonces podría estar absolutamente seguro de que coinciden, independientemente de las diferencias de tiempo.

Si desea que el relativo sea el correcto, quizás sea suficiente para que su registrador asigne un número de secuencia a cada mensaje en el orden en que se recibieron.

+0

Necesitaba marcas de tiempo porque necesito saber cuándo se recopilan los datos y para ver cuándo hay vacíos en los datos que se están produciendo. –

+0

Si su solución depende de la precisión del reloj, deberá encontrar un sistema operativo que garantice que su proceso sea siempre lo primero que suceda cuando lleguen los datos recopilados. De lo contrario, la programación del sistema operativo lo corregirá. –

5

Gracias a todos por sus contribuciones, todas han sido muy útiles. La respuesta de Brian parece más cercana a lo que eventualmente llegué (es decir, tratar con eso pero usar un tipo de identificador único, ver más abajo), así que acepté su respuesta. Logré consolidar todos los diversos receptores de datos en un único hilo, que es donde ahora se realiza el sellado de tiempo utilizando mi nueva clase AccurrateTimeStamp. Lo que he hecho funciona siempre que la marca de tiempo sea lo primero que use el reloj.

Como S.Lott estipula, sin un sistema operativo en tiempo real, nunca van a ser absolutamente perfectos. Realmente solo quería algo que me permitiera ver en relación con cada fragmento entrante de datos, cuando se recibían las cosas, por lo que lo que he hecho a continuación funcionará bien.

Gracias de nuevo a todos!

import time 

class AccurateTimeStamp(): 
    """ 
    A simple class to provide a very accurate means of time stamping some data 
    """ 

    # Do the class-wide initial time stamp to synchronise calls to 
    # time.clock() to a single time stamp 
    initialTimeStamp = time.time()+ time.clock() 

    def __init__(self): 
     """ 
     Constructor for the AccurateTimeStamp class. 
     This makes a stamp based on the current time which should be more 
     accurate than anything you can get out of time.time(). 
     NOTE: This time stamp will only work if nothing has called clock() in 
     this instance of the Python interpreter. 
     """ 
     # Get the time since the first of call to time.clock() 
     offset = time.clock() 

     # Get the current (accurate) time 
     currentTime = AccurateTimeStamp.initialTimeStamp+offset 

     # Split the time into whole seconds and the portion after the fraction 
     self.accurateSeconds = int(currentTime) 
     self.accuratePastSecond = currentTime - self.accurateSeconds 


def GetAccurateTimeStampString(timestamp): 
    """ 
    Function to produce a timestamp of the form "13:48:01.87123" representing 
    the time stamp 'timestamp' 
    """ 
    # Get a struct_time representing the number of whole seconds since the 
    # epoch that we can use to format the time stamp 
    wholeSecondsInTimeStamp = time.localtime(timestamp.accurateSeconds) 

    # Convert the whole seconds and whatever fraction of a second comes after 
    # into a couple of strings 
    wholeSecondsString = time.strftime("%H:%M:%S", wholeSecondsInTimeStamp) 
    fractionAfterSecondString = str(int(timestamp.accuratePastSecond*1000000)) 

    # Return our shiny new accurate time stamp 
    return wholeSecondsString+"."+fractionAfterSecondString 


if __name__ == '__main__': 
    for i in range(0,500): 
     timestamp = AccurateTimeStamp() 
     print GetAccurateTimeStampString(timestamp) 
0

Quería agradecer a J. Cage por esta última publicación.

Para mi trabajo, el tiempo "razonable" de eventos a través de procesos y plataformas es esencial. Obviamente, hay muchos lugares donde las cosas pueden ir mal (deriva del reloj, cambio de contexto, etc.); sin embargo, creo que esta precisa solución de temporización ayudará a garantizar que las marcas de tiempo registradas sean lo suficientemente precisas para ver las otras fuentes de error. .

Dicho esto, hay un par de detalles que me pregunto que se explican en When MicroSeconds Matter. Por ejemplo, creo que time.clock() finalmente se ajustará. Creo que para que esto funcione en un proceso de larga ejecución, es posible que tenga que manejar eso.

+0

De nada :-) –

1

Hace algunos años, desde que se formuló y se respondió la pregunta, y se ha resuelto, al menos para CPython en Windows.Usando la secuencia de comandos a continuación tanto en 64 bits Win7 y Windows Server 2008 R2, tengo los mismos resultados:

  • datetime.now() ofrece una resolución de 1 ms y una fluctuación menor que 1 ms
  • time.clock() da una resolución mejor que 1us y una fluctuación mucho menor que 1 ms

El guión:

import time 
import datetime 

t1_0 = time.clock() 
t2_0 = datetime.datetime.now() 

with open('output.csv', 'w') as f: 
    for i in xrange(100000): 
     t1 = time.clock() 
     t2 = datetime.datetime.now() 
     td1 = t1-t1_0 
     td2 = (t2-t2_0).total_seconds() 
     f.write('%.6f,%.6f\n' % (td1, td2)) 

Los resultados visualizados: enter image description here

+0

esto debe aceptarse respuesta –

0

Si desea microsecond- resolución (NO precisión) marcas de tiempo en Python, en de Windows, se puede usar el temporizador de QPC de Windows, como se demuestra en mi respuesta aquí: How to get millisecond and microsecond-resolution timestamps in Python. Todavía no estoy seguro de cómo hacer esto en Linux, así que si alguien sabe, por favor comente o responda en el enlace de arriba.

Cuestiones relacionadas