Pregunta

Recientemente he estado creando una aplicación de registro de errores y buscaba una forma de marcar con precisión la información entrante. Cuando digo con precisión, me refiero a que cada marca de tiempo debe ser precisa entre sí (no es necesario sincronizar con un reloj atómico ni nada de eso).

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

>>> 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 las muestras tienen este aspecto:

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-32 ms en mi máquina. El problema se presenta cuando analizamos los datos, ya que la clasificación por algo diferente a la marca de tiempo y luego la clasificación por fecha 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 dé una marca de tiempo única.

He estado considerando algunos métodos que involucran el uso de una llamada time.clock () agregada a una fecha y hora de inicio, pero apreciaría una solución que funcionaría con precisión en los subprocesos de la misma máquina. Cualquier sugerencia sería muy agradecida.

¿Fue útil?

Solución

Es poco probable que obtengas un control lo suficientemente preciso como para eliminar completamente la posibilidad de marcas de tiempo duplicadas: necesitaría una resolución más pequeña que el tiempo necesario para generar un objeto de fecha y hora. Hay un par de otros enfoques que puede tomar para lidiar con esto:

  1. Trata con eso. Deje sus marcas de tiempo no únicas como son, pero confíe en que Python sea estable para lidiar con los problemas de reordenación. Al ordenar en la marca de tiempo primero , algo más conservará el orden de la marca de tiempo; solo debe tener cuidado de comenzar siempre desde la lista ordenada de la marca de tiempo cada vez, en lugar de hacer varias ordenaciones en la misma lista.

  2. Agregue su propio valor para imponer la singularidad. P.ej. incluya un valor entero incremental como parte de la clave, o agregue dicho valor solo si las marcas de tiempo son diferentes. Por ejemplo.

Lo siguiente garantizará valores de marca de tiempo únicos:

    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 varios procesos (en lugar de subprocesos), se hace un poco más complicado.

Otros consejos

time.clock () solo mide el tiempo de wallclock en Windows. En otros sistemas, time.clock () en realidad 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 la que puede administrar Python, que es tan alta como puede administrar el sistema operativo; por lo general, usa gettimeofday (3) (resolución de microsegundos) o ftime (3) (resolución de milisegundos). Otras restricciones del sistema operativo 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 una resolución de 1/10000 segundos. Al mirar sus datos, tiene una resolución mucho más amplia que esa. No estoy seguro de que Python pueda hacer algo como eso, 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 el reloj de pared desde la primera llamada a time.clock (), así que debes recordar para 'inicializarlo' primero.

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

Como estipula S.Lott, 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 parte de los datos entrantes, cuando las cosas se recibían, así que lo que tengo 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)

" marca de tiempo debe ser precisa en relación con los demás "

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

¿Estás emparejando alguna fuente de información externa? ¿Dices un log en otra aplicación? Nuevamente, si hay una red, esos tiempos no serán demasiado cercanos.

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

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

Aquí hay un hilo sobre la precisión de la sincronización de Python:

Python - time.clock () vs. time.time (): ¿precisión?

Hace algunos años que pasaron la pregunta y la respuesta, y esto se ha tratado, al menos para CPython en Windows. Al usar la secuencia de comandos a continuación, tanto en Win7 64bit como en Windows Server 2008 R2, obtuve los mismos resultados:

  • datetime.now () ofrece una resolución de 1 ms y una fluctuación de fase inferior a 1 ms.
  • time.clock () ofrece una resolución mejor que 1us y un jitter mucho menor que 1ms

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: ingrese la descripción de la imagen aquí

Quería agradecer a J. Cage por este último post.

Para mi trabajo, " razonable " La sincronización de eventos a través de procesos y plataformas es esencial. Obviamente, hay muchos lugares donde las cosas pueden ir torcidas (cambio de reloj, cambio de contexto, etc.), sin embargo, creo que esta solución de sincronización precisa 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 sobre los que me pregunto que se explican en Cuando importan los microsegundos . Por ejemplo, creo que el tiempo.clock () finalmente se ajustará. Creo que para que esto funcione durante un largo proceso, es posible que tengas que manejarlo.

Si desea marcas de tiempo de microsegundos resolución (NO de precisión) en Python, en Windows, puede usar el temporizador QPC de Windows, como se demuestra en mi respuesta aquí: Cómo obtener marcas de tiempo de milisegundos y microsegundos en Python . Todavía no estoy seguro de cómo hacer esto en Linux, así que si alguien lo sabe, comente o responda en el enlace anterior.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top