tic, Toc fonctions analogiques en Python
Quel est le meilleur analogue des fonctions MATLAB tic et toc ( http://www.mathworks.com/help/techdoc/ref/tic.html ) en Python?
11 réponses
En dehors de timeit
que ThiefMaster a mentionné, un moyen simple de le faire est juste (après l'importation time
):
t = time.time()
# do stuff
elapsed = time.time() - t
J'ai une classe d'aide que j'aime utiliser:
class Timer(object):
def __init__(self, name=None):
self.name = name
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
if self.name:
print '[%s]' % self.name,
print 'Elapsed: %s' % (time.time() - self.tstart)
Il peut être utilisé comme gestionnaire de contexte:
with Timer('foo_stuff'):
# do some foo
# do some stuff
Parfois, je trouve cette technique plus pratique que timeit
- tout dépend de ce que vous voulez mesurer.
J'ai eu la même question quand J'ai migré vers Python depuis Matlab. Avec l'aide de ce thread, j'ai pu construire un analogue exact des fonctions Matlab tic()
et toc()
. Insérez simplement le code suivant en haut de votre script.
import time
def TicTocGenerator():
# Generator that returns time differences
ti = 0 # initial time
tf = time.time() # final time
while True:
ti = tf
tf = time.time()
yield tf-ti # returns the time difference
TicToc = TicTocGenerator() # create an instance of the TicTocGen generator
# This will be the main function through which we define both tic() and toc()
def toc(tempBool=True):
# Prints the time difference yielded by generator instance TicToc
tempTimeInterval = next(TicToc)
if tempBool:
print( "Elapsed time: %f seconds.\n" %tempTimeInterval )
def tic():
# Records a time in TicToc, marks the beginning of a time interval
toc(False)
C'est ça! Nous sommes maintenant prêts à utiliser pleinement tic()
et toc()
comme dans Matlab. Par exemple
tic()
time.sleep(5)
toc() # returns "Elapsed time: 5.00 seconds."
En fait, c'est plus polyvalent que les fonctions Matlab intégrées. Ici, vous pouvez créer une autre instance de le TicTocGenerator
pour garder une trace de plusieurs opérations, ou simplement pour chronométrer les choses différemment. Par exemple, tout en chronométrant un script, nous pouvons maintenant chronométrer chaque morceau du script séparément, ainsi que le script entier. (Je vais donner un exemple concret)
TicToc2 = TicTocGenerator() # create another instance of the TicTocGen generator
def toc2(tempBool=True):
# Prints the time difference yielded by generator instance TicToc2
tempTimeInterval = next(TicToc2)
if tempBool:
print( "Elapsed time 2: %f seconds.\n" %tempTimeInterval )
def tic2():
# Records a time in TicToc2, marks the beginning of a time interval
toc2(False)
Maintenant, vous devriez pouvoir chronométrer deux choses distinctes: dans l'exemple suivant, nous chronométrons le script total et les parties d'un script séparément.
tic()
time.sleep(5)
tic2()
time.sleep(3)
toc2() # returns "Elapsed time 2: 5.00 seconds."
toc() # returns "Elapsed time: 8.00 seconds."
En Fait, vous n'avez même pas besoin d'utiliser tic()
à chaque fois. Si vous avez une série de les commandes que vous voulez, alors vous pouvez écrire
tic()
time.sleep(1)
toc() # returns "Elapsed time: 1.00 seconds."
time.sleep(2)
toc() # returns "Elapsed time: 2.00 seconds."
time.sleep(3)
toc() # returns "Elapsed time: 3.00 seconds."
# and so on...
J'espère que cela est utile.
Le meilleur analogue absolu de tic et toc serait de simplement les définir en python.
def tic():
#Homemade version of matlab tic and toc functions
import time
global startTime_for_tictoc
startTime_for_tictoc = time.time()
def toc():
import time
if 'startTime_for_tictoc' in globals():
print "Elapsed time is " + str(time.time() - startTime_for_tictoc) + " seconds."
else:
print "Toc: start time not set"
, Puis vous pouvez les utiliser comme:
tic()
# do stuff
toc()
Habituellement, IPython %time
, %timeit
, %prun
et %lprun
(si l'on a line_profiler
installé) satisfait assez bien mes besoins de profilage. Cependant, un cas d'utilisation pour la fonctionnalité de type tic-toc
est apparu lorsque j'ai essayé de profiler des calculs qui étaient entraînés de manière interactive, c'est-à-dire par le mouvement de la souris de l'utilisateur dans une interface graphique. Je me sentais comme spamming tic
s et toc
s dans les sources tout en testant interactivement serait le moyen le plus rapide de révéler les goulots d'étranglement. Je suis allé avec la classe Timer
D'Eli Bendersky, mais n'était pas entièrement heureux, depuis il m'a fallu changer l'indentation de mon code, ce qui peut être gênant dans certains éditeurs et confond le système de contrôle de version. De plus, il peut être nécessaire de mesurer le temps entre les points dans différentes fonctions, ce qui ne fonctionnerait pas avec l'instruction with
. Après avoir essayé beaucoup D'intelligence Python, voici la solution simple qui a le mieux fonctionné:
from time import time
_tstart_stack = []
def tic():
_tstart_stack.append(time())
def toc(fmt="Elapsed: %s s"):
print fmt % (time() - _tstart_stack.pop())
Comme cela fonctionne en poussant les heures de départ sur une pile, cela fonctionnera correctement pour plusieurs niveaux de tic
s et toc
s. Cela permet également de changer la chaîne de format de l'instruction toc
pour afficher des informations supplémentaires, ce que j'ai aimé de la classe Timer
D'Eli.
Pour une raison quelconque, je me suis préoccupé de la surcharge d'une implémentation Python pure, j'ai donc testé un module d'extension C:
#include <Python.h>
#include <mach/mach_time.h>
#define MAXDEPTH 100
uint64_t start[MAXDEPTH];
int lvl=0;
static PyObject* tic(PyObject *self, PyObject *args) {
start[lvl++] = mach_absolute_time();
Py_RETURN_NONE;
}
static PyObject* toc(PyObject *self, PyObject *args) {
return PyFloat_FromDouble(
(double)(mach_absolute_time() - start[--lvl]) / 1000000000L);
}
static PyObject* res(PyObject *self, PyObject *args) {
return tic(NULL, NULL), toc(NULL, NULL);
}
static PyMethodDef methods[] = {
{"tic", tic, METH_NOARGS, "Start timer"},
{"toc", toc, METH_NOARGS, "Stop timer"},
{"res", res, METH_NOARGS, "Test timer resolution"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
inittictoc(void) {
Py_InitModule("tictoc", methods);
}
C'est pour MacOSX, et j'ai omis du code pour vérifier si lvl
est hors limites par souci de concision. Alors que tictoc.res()
donne une résolution d'environ 50 nanosecondes sur mon système, j'ai trouvé que la gigue de mesurer n'importe quelle instruction Python est facilement dans la gamme de microsecondes (et beaucoup plus lorsqu'il est utilisé à partir D'IPython). À ce stade, la surcharge de L'implémentation Python devient négligeable, de sorte qu'elle peut être utilisée avec la même confiance que L'implémentation C.
J'ai trouvé que l'utilité de l'approche tic-toc
est pratiquement limitée aux blocs de code qui prennent plus de 10 microsecondes à exécuter. En dessous de cela, des stratégies de moyenne comme dans timeit
sont nécessaires pour obtenir un fidèles de mesure.
Je viens de créer un module [tictoc.py] pour réaliser des TIC tocs imbriqués, Ce Que fait Matlab.
from time import time
tics = []
def tic():
tics.append(time())
def toc():
if len(tics)==0:
return None
else:
return time()-tics.pop()
Et cela fonctionne de cette façon:
from tictoc import tic, toc
# This keeps track of the whole process
tic()
# Timing a small portion of code (maybe a loop)
tic()
# -- Nested code here --
# End
toc() # This returns the elapse time (in seconds) since the last invocation of tic()
toc() # This does the same for the first tic()
J'espère que ça aide.
Jetez un oeil à la timeit
module.
Ce n'est pas vraiment équivalent mais si le code que vous voulez chronométrer est dans une fonction, vous pouvez facilement l'utiliser.
Juste au cas où quelqu'un serait intéressé. Sur la base de toutes les autres réponses, j'ai écrit une classe tictoc qui a le meilleur de toutes.
Le lien sur github est ici.
Vous pouvez également utiliser pip pour l'obtenir.
pip install ttictoc
Comme de comment l'utiliser:
Importez-le
from ttictoc import TicToc
En utilisant l'instruction 'with'
Sans créer d'objet, vous pouvez chronométrer votre code comme suit.
with TicToc('name'):
some code...
# Prints the elapsed time
Ou en créant un objet, vous pouvez faire de même.
t = TicToc('name')
with t:
some code...
# Prints the elapsed time
Appel tic table des matières explicitement
Vous pouvez également appeler le Tic toc explicitement comme indiqué ci-dessous.
t = TicToc('name')
t.tic()
some code...
t.toc()
print(t.elapsed)
With indentation
Si vous voulez chronométrer plusieurs niveaux de votre code, vous pouvez également le faire en définissant 'indentation' sur True.
t = TicToc(,indentation=True)
t.tic()
some code1...
t.tic()
some code2...
t.tic()
some code3...
t.toc()
print('time for code 3 ',t.elapsed)
t.toc()
print('time for code 2 with code 3 ',t.elapsed)
t.toc()
print('time for code 1 with code 2 and 3 ',t.elapsed)
Arguments
La classe A 3 arguments: nom, méthode et indentation.
- nom: C'est le nom de l'objet. Il n'est pas nécessaire.
- method: indique quelle méthode doit être utilisée pour obtenir l'heure.
- indentation: Permet d'utiliser le même objet plusieurs fois, dans différentes indentations au temps.
L'argument de la méthode peut être int, str ou votre choix de méthode. S'il s'agit d'une chaîne, les valeurs valides sont time, perf_counter et process_time. S'il s'agit d'un entier, les valeurs valides sont 0, 1 et 2.
- temps ou 0: Temps.temps
- perf_counter ou 1: Temps.perf_counter
- process_time ou 2: le temps.process_time
Si la version python > = 3.7: - time_ns ou 3: temps._temps - perf_counter_ns ou 4: temps.perf_counter_ns - process_time_ns ou 5: temps.process_time_ns
Dans le cas où vous préférez utiliser une autre méthode, vous faites juste (en utilisant comme exemple de temps.horloge:
TicToc(method=time.clock)
La classe est la suivante:
import sys
import time
class TicToc(object):
"""
Counts the elapsed time.
"""
def __init__(self,name='',method='time',indentation=False):
"""
Args:
name (str): Just informative, not needed
method (int|str|ftn|clss): Still trying to understand the default
options. 'time' uses the 'real wold' clock, while the other
two use the cpu clock. If you want to use your own method, do it
through this argument
Valid int values:
0: time.time | 1: time.perf_counter | 2: time.proces_time
if python version >= 3.7:
3: time.time_ns | 4: time.perf_counter_ns | 5: time.proces_time_ns
Valid str values:
'time': time.time | 'perf_counter': time.perf_counter
'process_time': time.proces_time
if python version >= 3.7:
'time_ns': time.time_ns | 'perf_counter_ns': time.perf_counter_ns
'proces_time_ns': time.proces_time_ns
Others:
Whatever you want to use as time.time
indentation (bool): Allows to do tic toc with indentation with a single object.
If True, you can put several tics using the same object, and each toc will
correspond to the respective tic.
If False, it will only register one single tic, and return the respective
elapsed time of the future tocs.
"""
self.name = name
self.indentation = indentation
if self.indentation:
self.tstart = []
self.__measure = 's' # seconds
self.__vsys = sys.version_info
if self.__vsys[0]>2 and self.__vsys[1]>=7:
# If python version is greater or equal than 3.7
if type(method) is int:
if method==0: method = 'time'
elif method==1: method = 'perf_counter'
elif method==2: method = 'process_time'
elif method==3: method = 'time_ns'
elif method==3: method = 'perf_counter_ns'
elif method==4: method = 'process_time_ns'
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
method = 'time'
if type(method) is str:
if method=='time': self.get_time = time.time
elif method=='perf_counter': self.get_time = time.perf_counter
elif method=='process_time': self.get_time = time.process_time
elif method=='time_ns': self.get_time = time.time_ns, self.__measure = 'ns' # nanoseconds
elif method=='perf_counter_ns': self.get_time = time.perf_counter_ns, self.__measure = 'ns' # nanoseconds
elif method=='process_time_ns': self.get_time = time.process_time_ns, self.__measure = 'ns' # nanoseconds
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
self.get_time = time.time
else:
self.get_time = method
else:
# If python vesion is lower than 3.7
if type(method) is int:
if method==0: method = 'time'
elif method==1: method = 'perf_counter'
elif method==2: method = 'process_time'
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
method = 'time'
if type(method) is str:
if method=='time': self.get_time = time.time
elif method=='perf_counter': self.get_time = time.perf_counter
elif method=='process_time': self.get_time = time.process_time
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
self.get_time = time.time
else:
self.get_time = method
def __enter__(self):
if self.indentation:
self.tstart.append(self.get_time())
else:
self.tstart = self.get_time()
def __exit__(self,type,value,traceback):
self.tend = self.get_time()
if self.indentation:
self.elapsed = self.tend - self.tstart.pop()
else:
self.elapsed = self.tend - self.tstart
if self.name!='': name = '[{}] '.format(self.name)
else: name = self.name
print('{0}Elapsed time: {1} ({2})'.format(name,self.elapsed,self.__measure))
def tic(self):
if self.indentation:
self.tstart.append(self.get_time())
else:
self.tstart = self.get_time()
def toc(self):
self.tend = self.get_time()
if self.indentation:
if len(self.tstart)>0:
self.elapsed = self.tend - self.tstart.pop()
else:
self.elapsed = None
else:
self.elapsed = self.tend - self.tstart
Cela peut également être fait en utilisant un wrapper. Manière très générale de garder le temps.
Le wrapper dans cet exemple de code encapsule n'importe quelle fonction et affiche le temps nécessaire pour exécuter la fonction:
def timethis(f):
import time
def wrapped(*args, **kwargs):
start = time.time()
r = f(*args, **kwargs)
print "Executing {0} took {1} seconds".format(f.func_name, time.time()-start)
return r
return wrapped
@timethis
def thistakestime():
for x in range(10000000):
pass
thistakestime()
J'ai un peu changé la réponse de @Eli Bendersky pour utiliser le ctor __init__()
et le dtor __del__()
pour faire le timing, afin qu'il puisse être utilisé plus commodément sans indenter le code original:
class Timer(object):
def __init__(self, name=None):
self.name = name
self.tstart = time.time()
def __del__(self):
if self.name:
print '%s elapsed: %.2fs' % (self.name, time.time() - self.tstart)
else:
print 'Elapsed: %.2fs' % (time.time() - self.tstart)
Pour utiliser, simple mettre minuterie ("blahblah") au début d'une portée locale. Le temps écoulé sera imprimé à la fin de la portée:
for i in xrange(5):
timer = Timer("eigh()")
x = numpy.random.random((4000,4000));
x = (x+x.T)/2
numpy.linalg.eigh(x)
print i+1
timer = None
, Il imprime:
1
eigh() elapsed: 10.13s
2
eigh() elapsed: 9.74s
3
eigh() elapsed: 10.70s
4
eigh() elapsed: 10.25s
5
eigh() elapsed: 11.28s
En M'appuyant sur les réponses de Stefan et antonimmo, j'ai fini par mettre
def Tictoc():
start_stack = []
start_named = {}
def tic(name=None):
if name is None:
start_stack.append(time())
else:
start_named[name] = time()
def toc(name=None):
if name is None:
start = start_stack.pop()
else:
start = start_named.pop(name)
elapsed = time() - start
return elapsed
return tic, toc
Dans un module utils.py
, et je l'utilise avec un
from utils import Tictoc
tic, toc = Tictoc()
Par ici
- , vous pouvez simplement utiliser
tic()
,toc()
et les imbriquer comme dans Matlab - Vous pouvez également les nommer:
tic(1)
,toc(1)
outic('very-important-block')
,toc('very-important-block')
et les minuteries avec des noms différents n'interféreront pas - leur importation de cette façon empêche les interférences entre les modules qui l'utilisent.
(ici, la table des matières n'affiche pas temps, mais le renvoie.)
Mettre à jour la réponse D'Eli à Python 3:
class Timer(object):
def __init__(self, name=None, filename=None):
self.name = name
self.filename = filename
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
message = 'Elapsed: %.2f seconds' % (time.time() - self.tstart)
if self.name:
message = '[%s] ' % self.name + message
print(message)
if self.filename:
with open(self.filename,'a') as file:
print(str(datetime.datetime.now())+": ",message,file=file)
Tout comme Eli, il peut être utilisé comme gestionnaire de contexte:
import time
with Timer('Count'):
for i in range(0,10_000_000):
pass
Sortie:
[Count] Elapsed: 0.27 seconds
Je l'ai également mis à jour pour imprimer les unités de temps signalées (secondes) et réduire le nombre de chiffres comme suggéré par Can, et avec la possibilité d'ajouter également à un fichier journal. Vous devez importer datetime pour utiliser la fonction de journalisation:
import time
import datetime
with Timer('Count', 'log.txt'):
for i in range(0,10_000_000):
pass