Question

Je construis depuis peu une application de consignation des erreurs et je cherchais un moyen d’horodatage précis des données entrantes. Lorsque je dis avec précision, je veux dire que chaque horodatage doit être précis l’un par rapport à l’autre (nul besoin de synchroniser une horloge atomique ni quoi que ce soit de ce genre).

J'utilise datetime.now () comme premier coup de poignard, mais ce n'est pas parfait:

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

Les changements d'horloge pour la première seconde d'échantillons ressemblent à ceci:

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

Il semble donc que les données de la minuterie ne sont mises à jour que toutes les 15 à 32 ms sur ma machine. Le problème survient lorsque nous analysons les données, car le tri par autre chose que l'horodatage, puis à nouveau par horodatage peut laisser les données dans le mauvais ordre (chronologiquement). Il serait bien que les horodatages soient précis au point que tout appel au générateur d’horodatage donne un horodatage unique.

J'avais envisagé certaines méthodes impliquant l'utilisation d'un appel time.clock () ajouté à une date / heure de début, mais j'apprécierais une solution qui fonctionnerait avec précision sur plusieurs threads sur la même machine. Toute suggestion serait très chaleureusement reçue.

Était-ce utile?

La solution

Il est peu probable que vous obteniez un contrôle suffisamment fin pour pouvoir éliminer complètement la possibilité des horodatages en double - il vous faudrait une résolution inférieure au temps nécessaire pour générer un objet datetime. Il existe deux autres solutions que vous pourriez adopter pour y faire face:

  1. Traitez-le. Laissez vos horodatages non-uniques tels qu'ils sont, mais reposez-vous sur le caractère stable de python pour traiter les problèmes de réorganisation. Triez d'abord d'abord sur l'horodatage, puis quelque chose d'autre conservera l'ordre de l'horodatage - vous devez simplement faire attention à toujours démarrer à partir de la liste ordonnée d'horodatage à chaque fois, plutôt que de faire plusieurs tris sur la même liste.

  2. Ajoutez votre propre valeur pour renforcer l'unicité. Par exemple. incluez une valeur entière incrémentante dans la clé ou ajoutez-la uniquement si les horodatages sont différents. Par exemple,

Les éléments suivants garantissent des valeurs d’horodatage uniques:

    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

Pour plusieurs processus (plutôt que des threads), cela devient un peu plus compliqué.

Autres conseils

time.clock () ne mesure que l'heure de l'horloge murale sous Windows. Sur d'autres systèmes, time.clock () mesure en réalité le temps CPU. Sur ces systèmes, time.time () est plus approprié pour l'heure wallclock et sa résolution est aussi élevée que celle que Python peut gérer - ce qui est aussi élevé que le système d'exploitation ne peut la gérer. utilisez habituellement gettimeofday (3) (résolution en microsecondes) ou ftime (3) (résolution en millisecondes.) D'autres restrictions du système d'exploitation rendent la résolution réelle beaucoup plus élevée que cela. datetime.datetime.now () utilise time.time (), donc time.time () ne sera pas meilleur directement.

Pour mémoire, si j'utilise datetime.datetime.now () dans une boucle, je vois une résolution d'environ 1/10000 seconde. En regardant vos données, vous avez une résolution beaucoup, beaucoup plus grossière que cela. Je ne sais pas si Python en tant que tel peut faire quoi que ce soit, même si vous pouvez convaincre le système d'exploitation de faire mieux par d'autres moyens.

Il me semble que, sous Windows, time.clock () est en réalité (légèrement) plus précis que time.time (), mais qu'il mesure wallclock depuis le premier appel à time.clock (), vous devez donc vous en souvenir. pour "l'initialiser" en premier.

Merci à tous pour vos contributions - elles ont toutes été très utiles. La réponse de Brian semble se rapprocher de ce que j’ai finalement choisi (c’est-à-dire l’utiliser mais utiliser une sorte d’identificateur unique - voir ci-dessous) et j’ai donc accepté sa réponse. J'ai réussi à regrouper tous les récepteurs de données dans un seul thread. C'est ici que l'horodatage est maintenant effectué à l'aide de ma nouvelle classe AccurrateTimeStamp . Ce que j'ai fait fonctionne tant que l'horodatage est la première chose à utiliser l'horloge.

Comme le stipule S.Lott, sans système d’exploitation temps réel, ils ne seront jamais absolument parfaits. Je ne voulais vraiment que quelque chose qui me laisse voir par rapport à chaque bloc de données entrant, lorsque les éléments étaient reçus, donc ce que j'ai ci-dessous fonctionnera bien.

Merci encore à tous!

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)

"l'horodatage doit être précis l'un par rapport à l'autre"

Pourquoi le temps? Pourquoi pas un numéro de séquence? S'il s'agit d'un client d'application serveur-serveur, la latence du réseau rend les horodatages aléatoires.

Faites-vous correspondre une source d’information externe? Dites un journal sur une autre application? Encore une fois, s'il y a un réseau, ces temps ne seront pas trop proches.

Si vous devez faire correspondre des éléments entre des applications distinctes, envisagez de passer des GUID afin que les deux applications enregistrent la valeur du GUID. Ensuite, vous pouvez être absolument sûr qu'ils correspondent, quelles que soient les différences de timing.

Si vous souhaitez que l'ordre relatif soit exactement correct, il suffit peut-être que votre enregistreur attribue un numéro de séquence à chaque message dans l'ordre de réception.

Voici un fil de discussion sur la précision de la synchronisation Python:

Python-time.clock () contre time.time (): exactitude?

Il y a quelques années, la question a été posée et une réponse a été apportée, du moins pour CPython sous Windows. En utilisant le script ci-dessous sous Win7 64 bits et Windows Server 2008 R2, j'ai obtenu les mêmes résultats:

  • datetime.now () donne une résolution de 1 ms et une gigue inférieure à 1 ms
  • time.clock () donne une résolution meilleure que 1us et une gigue beaucoup plus petite que 1ms

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

Les résultats visualisés: entrer la description de l'image ici

Je voulais remercier J. Cage pour ce dernier post.

Pour mon travail, "raisonnable" La synchronisation des événements entre processus et plates-formes est essentielle. Il y a évidemment beaucoup d'endroits où les choses peuvent aller de travers (dérive de l'horloge, changement de contexte, etc.), cependant, cette solution de minutage précis contribuera, je pense, à garantir que les horodatages enregistrés sont suffisamment précis pour identifier les autres sources d'erreur. .

Cela dit, je m'interroge sur quelques détails qui sont expliqués dans le titre Lorsque les micro-secondes sont importantes . Par exemple, je pense que time.clock () finira par s’emballer. Je pense que pour que cela fonctionne pendant un long processus, vous devrez peut-être gérer cela.

Si vous souhaitez des horodatages en microsecondes résolution (PAS de précision) en Python, dans Windows, , vous pouvez utiliser le temporisateur QPC de Windows, comme indiqué dans ma réponse ici: . Je ne sais pas encore comment faire cela sous Linux. Si quelqu'un le sait, merci de commenter ou de répondre via le lien ci-dessus.

scroll top