Точная отметка времени в протоколе Python
Недавно я создавал приложение для регистрации ошибок, а затем использовал метод точной маркировки входящих данных. Когда я говорю точно, я имею в виду, что каждая метка времени должна быть точной относительно друг друга (нет необходимости синхронизировать с атомными часами или что-то в этом роде).
Я использовал datetime.now() как первый удар, но это не идеально:
>>> 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.
Изменения между часами для первой секунды образцов выглядят следующим образом:
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
Итак, похоже, что данные таймера обновляются только каждые ~ 15-32мс на моей машине. Проблема возникает, когда мы приходим к анализу данных, потому что сортировка с помощью чего-то другого, кроме отметки времени, а затем сортировка по метке времени снова может оставить данные в неправильном порядке (в хронологическом порядке). Было бы неплохо иметь временные метки с точностью до точки, что любой вызов генератора метки времени дает уникальную метку времени.
Я рассматривал некоторые методы, связанные с использованием вызова time.clock(), добавленного в начало datetime, но был бы признателен за решение, которое будет работать точно по потокам на одной машине. Любые предложения были бы очень благодарны.
Ответы
Ответ 1
Вы вряд ли получите достаточно мелкозернистый контроль, чтобы полностью исключить возможность
повторяющихся временных меток - вам потребуется разрешение меньше времени, необходимого для создания объекта datetime. Есть несколько других подходов, которые вы можете предпринять, чтобы справиться с этим:
-
Сделка с этим. Оставьте свои временные метки не уникальными, как они есть, но полагайтесь на то, что тип python является стабильным, чтобы справляться с проблемами переупорядочения. Сначала сначала сортировка по метке времени, затем что-то еще сохранит настройку timestamp - вам просто нужно быть осторожным, чтобы всегда начинать с упорядоченного списка timestamp каждый раз, а не делать несколько сортировок в одном списке.
-
Добавляйте собственное значение для обеспечения уникальности. Например. включают в себя добавочное целочисленное значение как часть ключа или добавление такого значения только в том случае, если метки времени разные. Например.
Следующие значения гарантируют уникальные значения временной метки:
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
Для нескольких процессов (а не для потоков) это становится немного сложнее.
Ответ 2
time.clock() измеряет только время работы в Windows. В других системах время .clock() фактически измеряет время CPU. В этих системах time.time() больше подходит для времени настенного времени, и он имеет такое высокое разрешение, которое может управлять Python, - что выше, чем может управлять ОС; обычно используя gettimeofday (3) (разрешение в микросекундах) или ftime (3) (разрешение в миллисекундах). Другие ограничения ОС фактически делают реальное разрешение намного выше, чем это. datetime.datetime.now() использует time.time(), поэтому time.time() напрямую не будет лучше.
Для записи, если я использую datetime.datetime.now() в цикле, я вижу примерно 1/10000 секундное разрешение. От взгляда на ваши данные у вас есть намного, гораздо более грубое разрешение, чем это. Я не уверен, что что-то может сделать Python как таковой, хотя вы можете убедить ОС сделать лучше с помощью других средств.
Кажется, я вспоминаю, что в Windows время .clock() на самом деле (немного) точнее, чем time.time(), но оно измеряет стену с момента первого вызова time.clock(), поэтому вам нужно помнить сначала инициализировать его.
Ответ 3
Спасибо всем за ваш вклад - все они очень полезны. Ответ Брайана кажется самым близким к тому, с чем я в конечном итоге пошел (например, справиться с ним, но использовать своего рода уникальный идентификатор - см. Ниже), поэтому я принял его ответ. Мне удалось объединить все различные приемники данных в один поток, в котором теперь выполняется временная привязка, используя мой новый класс AccurrateTimeStamp. То, что я сделал, работает, пока метка времени - это первое, что нужно использовать для часов.
Как указывает S.Lott, без ОС реального времени они никогда не будут абсолютно идеальными. Я действительно хотел только что-то, что позволило бы мне видеть по отношению к каждому входящему фрагменту данных, когда что-то получалось, а то, что у меня было ниже, будет хорошо работать.
Еще раз спасибо всем!
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)
Ответ 4
"временная метка должна быть точной относительно друг друга"
Почему время? Почему не порядковый номер? Если это клиент клиент-серверного приложения, задержка в сети делает временные отметки случайными.
Соответствует ли вам какой-то внешний источник информации? Произнесите журнал в другом приложении? Опять же, если есть сеть, то время не будет слишком близко.
Если вы должны соответствовать вещам между отдельными приложениями, подумайте о прохождении GUID, чтобы оба приложения регистрировали значение GUID. Тогда вы можете быть абсолютно уверены, что они совпадают, независимо от временных разниц.
Если вы хотите, чтобы относительный порядок был правильным, может быть, достаточно, чтобы ваш регистратор присвоил порядковый номер каждому сообщению в том порядке, в котором они были получены.
Ответ 5
Вот поток о точности синхронизации Python:
Python - time.clock() vs. time.time() - точность?
Ответ 6
Несколько лет назад, когда был задан вопрос и ответили, и это было рассмотрено, по крайней мере, для CPython в Windows. Используя script ниже как на Win7 64bit, так и на Windows Server 2008 R2, я получил те же результаты:
-
datetime.now()
дает разрешение 1 мс и дрожание меньше 1 мс
-
time.clock()
дает разрешение лучше, чем 1us, и дрожание намного меньше, чем 1 мс
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))
Результаты визуализируются:
![enter image description here]()
Ответ 7
Я хотел поблагодарить Дж. Кейджа за этот последний пост.
Для моей работы важно "разумное" время событий в процессах и платформах. Очевидно, что есть много мест, где все может идти в ногу (дрейф часов, переключение контекста и т.д.), Однако это точное решение по срокам, я думаю, поможет обеспечить, чтобы записанные временные метки были достаточно точными, чтобы увидеть другие источники ошибок,
Тем не менее, есть несколько деталей, о которых мне интересно, об этом объясняется в Когда MicroSeconds Matter. Например, я думаю, что time.clock() в конечном итоге будет завершен. Я думаю, для этого, чтобы работать в течение длительного времени, вам, возможно, придется справиться с этим.
Ответ 8
Если вы хотите использовать отметки времени с микросекундным разрешением (NOT precision) в Python, в Windows вы можете использовать таймер QPS для Windows, как показано в моем ответе здесь: Как получить миллисекунду и микросекунду -resolution timestamps в Python. Я не уверен, как это сделать в Linux, но если кто-нибудь знает, прокомментируйте или ответьте по ссылке выше.