2011-09-14 20 views
46

¿Hay un modo/módulo simple para correctamente medir el tiempo transcurrido en python? Sé que puedo simplemente llamar al time.time() dos veces y tomar la diferencia, pero eso arrojará resultados incorrectos si se cambia la hora del sistema. Por supuesto, eso no sucede muy a menudo, pero indica que estoy midiendo lo incorrecto.Medición del tiempo transcurrido en python

El uso de time.time() para medir duraciones es increíblemente indirecto cuando lo piensas. Usted toma la diferencia de dos mediciones de tiempo absoluto que a su vez están construidas a partir de medidas de duración (realizadas por temporizadores) y tiempos absolutos conocidos (establecidos manualmente o mediante ntp), que no le interesan en absoluto.

Entonces, ¿hay alguna manera de consultar este "tiempo del temporizador" directamente? Me imagino que se puede representar como un valor de milisegundos o microsegundos que no tiene una representación absoluta significativa (y por lo tanto no necesita ajustarse con la hora del sistema). Mirando un poco, parece que esto es exactamente lo que System.nanoTime() hace en Java, pero no encontré una función Python correspondiente, aunque debería ser (hardware-technical) más fácil de proporcionar que time.time().

Editar: Para evitar confusiones y abordar las respuestas a continuación: Esto no se trata de cambios de horario de verano, y tampoco quiero tiempo de CPU - Deseo tiempo físico transcurrido. No necesita ser muy refinado, ni siquiera particularmente preciso. Simplemente no debería darme duraciones negativas, o duraciones que están apagadas por varios órdenes de magnitud (por encima de la granularidad), solo porque alguien decidió configurar el reloj del sistema a un valor diferente. Esto es lo que la documentación de Python dicen sobre 'time.time()':

"While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls"

Esto es exactamente lo que quiero evitar, ya que puede llevar a cosas extrañas como valores negativos en los cálculos de tiempo. Puedo solucionar esto en este momento, pero creo que es una buena idea aprender a usar las soluciones adecuadas donde sea posible, ya que los kludges volverán a morderlo algún día.

Edit2: Algunas investigaciones muestran que puede obtener una medición independiente del tiempo del sistema como la que quiero en Windows usando GetTickCount64(), en Linux puede obtenerla en el valor de retorno de times(). Sin embargo, todavía no puedo encontrar un módulo que proporcione esta funcionalidad en Python.

+2

Si quiere hacer un benchmark de algo, debe usar 'timeit'. – delnan

+1

relacionado: [¿Cómo puedo obtener duraciones de tiempo monótonas en python?] (Http://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python) – jfs

Respuesta

10

Lo que parece que está buscando es un monotonic timer. Un monotonic time reference no salta ni retrocede.

Ha habido varios intentos de implementar un reloj monotómico multiplataforma para Python basado en la referencia del sistema operativo de la misma. (Windows, POSIX y BSD son bastante diferentes) Consulte las discusiones y algunos de los intentos en tiempo monotónico en this SO post.

Sobre todo, sólo puede utilizar os.times():

os.times()

devolver un 5-tupla de números de puntos que indican acumulada (procesador u otros) veces flotante, en segundos. Los ítems son: hora del usuario, hora del sistema, hora del usuario de los niños, hora del sistema de niños, y tiempo real transcurrido desde un punto fijo en el pasado, en ese orden. Consulte la página del manual de Unix veces (2) o la correspondiente documentación de la API de plataforma de Windows . En Windows, solo los primeros dos elementos están llenos,, los demás son cero.

Disponibilidad: Unix, Windows

Pero eso no llena en el tiempo real transcurrido necesario (el quinto tupla) en Windows.

Si necesita soporte de Windows, considere ctypes y puede llamar a GetTickCount64() directamente, como se ha hecho en this recipe.

+1

Me pregunto por qué nadie ha propuesto agregar el quinto valor de tupla a os.times en Windows? Parece que debería ser posible, ya que el punto de referencia no está especificado. –

+0

@Mark Ransome: No es un tipo Winoze, pero tal vez porque algo como 'GetTickCount64()' o la versión 32 de lo mismo no está en la API principal de Windows que se asume en la línea de base para cPython? es decir, soporte heredado en esa plataforma? No sé de otra manera. La referencia de tiempo monotónica es una característica útil. –

24

Para medir el tiempo de CPU transcurrido, mira time.clock(). Este es el equivalente del campo de tiempo de usuario de times() de Linux.

Para la evaluación comparativa, use timeit.

El datetime module, which is part of Python 2.3+, también tiene un tiempo de microsegundo si es compatible con la plataforma.

Ejemplo:

>>> import datetime as dt 
>>> n1=dt.datetime.now() 
>>> n2=dt.datetime.now() 
>>> (n2-n1).microseconds 
678521 
>>> (n2.microsecond-n1.microsecond)/1e6 
0.678521 
ie, it took me .678521 seconds to type the second n2= line -- slow 
>>> n1.resolution 
datetime.timedelta(0, 0, 1) 
1/1e6 resolution is claimed. 

Si usted está preocupado por los cambios de tiempo del sistema (de DS -> ST) acaba de comprobar el objeto devuelto por datetime.Presumably, la hora del sistema podría tener un pequeño ajuste de una referencia NTP ajuste. This should be slewed, y las correcciones son applied gradually, pero ntp sync beats puede tener un efecto con referencias de tiempo muy pequeñas (milisegundos o microsec).

También puede hacer referencia a Alex Martelli's C function si quiere algo de esa resolución. No iría demasiado lejos para reinventar la rueda. El tiempo preciso es básico y la mayoría de los SO modernos hacen un trabajo bastante bueno.

Editar

Sobre la base de sus aclaraciones, suena como que necesita una comprobación simple de lado si el reloj del sistema ha cambiado. Basta con comparar a un servidor NTP amable, locales:

import socket 
import struct 
import time 

ntp="pool.ntp.org" # or whatever ntp server you have handy 

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
data = '\x1b' + 47 * '\0' 
client.sendto(data, (ntp, 123)) 
data, address = client.recvfrom(1024) 
if data: 
    print 'Response received from:', address 
    t = struct.unpack('!12I', data)[10] 
    t -= 2208988800L #seconds since Epoch 
    print '\tTime=%s' % time.ctime(t) 

NTP tiene una precisión de milisegundos a través de Internet y tiene una resolución de representación de la resolución de 2-32 segundos (233 picosegundos). Debería ser lo suficientemente bueno?

Tenga en cuenta que la estructura de datos de bits NTP 64 will overflow in 2036 and every 136 years thereafter - si realmente quieres una solución robusta, mejor cheque por desbordamiento ...

+1

+1 para la información de giro, que mitiga el problema. Sin embargo, no estoy buscando mayor granularidad. De hecho, en mi aplicación actual, los segundos estarían bien. – Medo42

+2

No creo que una consulta de servidor NTP sea una solución elegante. Después de todo, hay hardware dedicado para medir lo que quiero construir en todas las PC, y puede ser consultado por las funciones del sistema tanto en Windows como en Linux - vea mi nueva edición anterior. – Medo42

+0

@ Medo42: No necesitaría hacer la llamada ntp a cada instancia del temporizador; solo si 'time.time()' devolvió un número negativo o algo inesperado. [Pyson's] (http://stackoverflow.com/questions/7421641/measuring-elapsed-time-in-python/7423817#7423817) el método de comparación con 'time.clock()' tiene mérito. Podría usar algo así para activar cuándo hacer una comparación con una fuente externa. Personalmente, el tiempo del sistema operativo es lo suficientemente bueno para milisegundos + tiempo para mí. El uso de un temporizador HW tiene sus propios problemas de precisión. – dawg

4
>>> import datetime 
>>> t1=datetime.datetime.utcnow() 
>>> t2=datetime.datetime.utcnow() 
>>> t2-t1 
datetime.timedelta(0, 8, 600000) 

Usando UTC evita esos períodos embarazosas cuando el reloj se desplaza debido a la horario de verano.

En cuanto a utilizar un método alternativo en lugar de restar dos relojes, tenga en cuenta que el sistema operativo realmente contiene un reloj que se inicializa desde un reloj de hardware en la PC. Las implementaciones modernas del sistema operativo también mantendrán ese reloj sincronizado con alguna fuente oficial para que no se desplace. Esto es mucho más preciso que cualquier temporizador de intervalo que la PC pueda estar ejecutando.

+1

Más preciso, sí, a largo plazo. Este tiempo del reloj puede comportarse de manera extraña en el instante en que se ajusta (a menos que el ajuste se desplace, como se describe en la respuesta de Drawk).No necesito una gran precisión durante mucho tiempo, pero quiero mediciones fiables a corto plazo que no den intervalos negativos o demasiado grandes simplemente porque alguien decidió cambiar manualmente la hora del sistema, lo que sucede a veces. – Medo42

2

Las funciones de ejemplo que indicar en su edición son dos cosas completamente diferentes: los tiempos

  1. Linux proceso times() retornos en milisegundos de CPU. El equivalente de Python es time.clock() o os.times().
  2. Windows GetTickCount64() devuelve el tiempo de actividad del sistema.

Aunque dos funciones diferentes, ambos (potencialmente) podrían usarse para revelar un reloj de sistema que tenía un "eructo" con estos métodos:

Primero:

usted podría tomar tanto una sistema de tiempo con time.time() y un tiempo de CPU con time.clock(). Como el tiempo del reloj de pared SIEMPRE será mayor o igual que el tiempo de la CPU, descarte cualquier medida en la que el intervalo entre las dos lecturas time.time() sea menor que las lecturas de verificación del time.clock() emparejado.

Ejemplo:

t1=time.time() 
t1check=time.clock() 

# your timed event...  

t2=time.time() 
t2check=time.clock() 

if t2-t1 < t2check - t1check: 
    print "Things are rotten in Denmark" 
    # discard that sample 
else: 
    # do what you do with t2 - t1... 

Segundo:

Conseguir el tiempo de actividad del sistema también es prometedor si usted está preocupado por el reloj del sistema, ya que un restablecimiento de usuario no restablece el recuento de tiempo de actividad de la garrapata en la mayoría de los casos . (que conozco ...)

Ahora la pregunta más difícil: obtener el tiempo de actividad del sistema de una manera independiente de la plataforma, especialmente sin generar un nuevo caparazón, con la precisión del segundo segundo. Hmmm ...

Probablemente la mejor apuesta es psutil. Browsing the source, usan uptime = GetTickCount()/1000.00f; para Windows y sysctl "kern.boottime" para BSD/OS X, etc. Desafortunadamente, estas son todas de 1 segundo de resolución.

+0

También estaba confundido acerca de los tiempos() al principio, pero lea su propio manual vinculado (particularmente la parte que dice "Valor devuelto"). La función se puede usar para buscar tiempos de proceso que se llenan en una estructura proporcionada, pero * devuelve * un reloj del sistema independientemente de la medida del tiempo. – Medo42

+0

@ Medo42: Tienes razón, mi mal. –

+0

¿Es cierto que el tiempo del reloj de pared siempre será mayor que el tiempo de la CPU? Creo que este es solo el caso de los sistemas de núcleo único. – Alex

2
from datetime import datetime 
start = datetime.now() 
print 'hello' 
end = datetime.now() 
delta = end-start 
print type(delta) 
<type 'datetime.timedelta'> 
import datetime 
help(datetime.timedelta) 
...elapsed seconds and microseconds... 
Cuestiones relacionadas