Domanda

Di recente ho creato un'app per la registrazione degli errori e cercavo in modo accurato il timestamp dei dati in arrivo. Quando dico con precisione intendo che ogni timestamp deve essere accurato l'uno rispetto all'altro (non è necessario sincronizzarsi con un orologio atomico o qualcosa del genere).

Ho usato datetime.now () come primo colpo, ma questo non è perfetto:

>>> 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.

Le modifiche tra gli orologi per il primo secondo dei campioni sono simili alle seguenti:

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

Quindi sembra che i dati del timer vengano aggiornati solo ogni ~ 15-32ms sulla mia macchina. Il problema si presenta quando arriviamo ad analizzare i dati perché l'ordinamento per qualcosa di diverso dal timestamp e quindi l'ordinamento di nuovo in base al timestamp può lasciare i dati nell'ordine sbagliato (cronologicamente). Sarebbe bello avere i timestamp accurati al punto che qualsiasi chiamata al generatore di timestamp dà un timestamp unico.

Avevo preso in considerazione alcuni metodi che prevedevano l'uso di una chiamata time.clock () aggiunta a un datetime iniziale, ma apprezzerei una soluzione che funzionasse con precisione tra i thread sulla stessa macchina. Eventuali suggerimenti saranno ricevuti con gratitudine.

È stato utile?

Soluzione

È improbabile che tu ottenga un controllo sufficientemente preciso da poter eliminare completamente la possibilità di timestamp duplicati: avrai bisogno di una risoluzione inferiore al tempo necessario per generare un oggetto datetime. Ci sono un paio di altri approcci che potresti adottare per affrontarlo:

  1. Affrontalo. Lascia i tuoi timestamp non unici come sono, ma fai affidamento sul fatto che il tipo di Python sia stabile per gestire i problemi di riordino. Ordinando il timestamp prima , poi qualcos'altro manterrà l'ordinamento del timestamp - devi solo fare attenzione a iniziare sempre dall'elenco di timestamp ordinato ogni volta, anziché fare più ordinamenti nello stesso elenco.

  2. Aggiungi il tuo valore per rafforzare l'unicità. Per esempio. includere un valore intero incrementale come parte della chiave o aggiungere tale valore solo se i timestamp sono diversi. Eg.

Quanto segue garantirà valori univoci di data / ora:

    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

Per più processi (piuttosto che thread), però diventa un po 'più complicato.

Altri suggerimenti

time.clock () misura solo il tempo di wallclock su Windows. Su altri sistemi, time.clock () misura effettivamente il tempo della CPU. Su questi sistemi time.time () è più adatto al tempo di wallclock e ha una risoluzione alta quanto Python è in grado di gestire - che è alta quanto il sistema operativo può gestire; di solito usando gettimeofday (3) (risoluzione microsecondi) o ftime (3) (risoluzione millisecondi). Altre restrizioni del sistema operativo rendono la risoluzione reale molto più elevata di quella. datetime.datetime.now () utilizza time.time (), quindi time.time () direttamente non sarà migliore.

Per la cronaca, se uso datetime.datetime.now () in un ciclo, vedo una risoluzione di 1/10000 di secondo. Guardando i tuoi dati, hai una risoluzione molto, molto più grossolana di quella. Non sono sicuro che ci sia qualcosa che Python possa fare, anche se potresti essere in grado di convincere il sistema operativo a fare meglio con altri mezzi.

Mi sembra di ricordare che su Windows, time.clock () è in realtà (leggermente) più preciso di time.time (), ma misura wallclock dalla prima chiamata a time.clock (), quindi devi ricordare per "inizializzarlo" per primo.

Grazie a tutti per i vostri contributi: sono stati tutti molto utili. La risposta di Brian sembra la più vicina a ciò che alla fine sono andato (cioè affrontarlo ma usare una sorta di identificatore univoco - vedi sotto) quindi ho accettato la sua risposta. Sono riuscito a consolidare tutti i vari ricevitori di dati in un singolo thread che è dove ora viene eseguito il timestamp utilizzando la mia nuova classe AccurrateTimeStamp . Quello che ho fatto funziona fintanto che il timestamp è la prima cosa da usare l'orologio.

Come prevede S.Lott, senza un sistema operativo in tempo reale, non saranno mai assolutamente perfetti. Volevo davvero solo qualcosa che mi permettesse di vedere in relazione a ciascun blocco di dati in entrata, quando le cose venivano ricevute, quindi quello che ho sotto funzionerà bene.

Grazie ancora a tutti!

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)

" il timestamp deve essere accurato l'uno rispetto all'altro "

Perché il tempo? Perché non un numero progressivo? Se è un client dell'applicazione client-server, la latenza della rete rende i timestamp un po 'casuali.

Stai abbinando qualche fonte di informazione esterna? Dì un registro su un'altra applicazione? Ancora una volta, se c'è una rete, quei tempi non saranno troppo vicini.

Se devi abbinare elementi tra app separate, considera di passare i GUID in modo che entrambe le app registrino il valore GUID. Quindi potresti essere assolutamente sicuro che corrispondano, indipendentemente dalle differenze di tempismo.

Se vuoi che l'ordine relativo sia esattamente corretto, forse è sufficiente che il tuo logger assegni un numero progressivo a ciascun messaggio nell'ordine in cui sono stati ricevuti.

Ecco un thread sull'accuratezza dei tempi di Python:

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

Pochi anni dopo la domanda e la risposta, e questo è stato risolto, almeno per CPython su Windows. Utilizzando lo script seguente sia su Win7 64 bit che su Windows Server 2008 R2, ho ottenuto gli stessi risultati:

  • datetime.now () fornisce una risoluzione di 1 ms e un jitter inferiore a 1 ms
  • time.clock () fornisce una risoluzione migliore di 1us e un jitter molto più piccolo di 1ms

Lo script:

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

I risultati visualizzati: inserisci qui la descrizione dell'immagine

Volevo ringraziare J. Cage per questo ultimo post.

Per il mio lavoro, "ragionevole" il tempismo degli eventi tra processi e piattaforme è essenziale. Ovviamente ci sono molti posti in cui le cose possono andare storte (deriva dell'orologio, cambio di contesto, ecc.), Tuttavia questa soluzione di temporizzazione accurata, credo, aiuterà a garantire che i timestamp registrati siano sufficientemente accurati per vedere le altre fonti di errore .

Detto questo, ci sono un paio di dettagli di cui mi chiedo che sono spiegati nel When MicroSeconds Matter . Ad esempio, penso che time.clock () finirà per finire. Penso che affinché funzioni per un lungo processo, potresti doverlo gestire.

Se vuoi i timestamp di microsecondo- risoluzione (NON precisione) in Python, in Windows puoi usare il timer QPC di Windows, come dimostrato nella mia risposta qui: Come ottenere i timestamp con risoluzione in millisecondi e microsecondi in Python . Non sono ancora sicuro di come farlo in Linux, quindi se qualcuno lo sa, per favore commenta o rispondi nel link sopra.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top