Quel est le meilleur analogue des fonctions MATLAB tic et toc ( http://www.mathworks.com/help/techdoc/ref/tic.html ) en Python?
Mis à part timeit
mentionné par ThiefMaster, un moyen simple de le faire est simplement (après avoir importé time
):
t = time.time()
# do stuff
elapsed = time.time() - t
J'ai un cours 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'avais la même question lorsque j'ai migré vers Python à partir de Matlab. Avec l'aide de ce fil, j'ai pu construire un exact analogue 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 tout! 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, cela est plus polyvalent que les fonctions Matlab intégrées. Ici, vous pouvez créer une autre instance de TicTocGenerator
pour suivre 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 partie 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)
Vous devriez maintenant 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 voulez programmer une série de commandes, 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 c'est utile.
Le meilleur analogue absolu entre tic et toc serait simplement de 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"
Ensuite, vous pouvez les utiliser comme:
tic()
# do stuff
toc()
Normalement, les %time
, %timeit
, %prun
et %lprun
(si line_profiler
est installé) d'IPython satisfont assez bien à mes besoins en matière de profilage. Cependant, un cas d’utilisation de la fonctionnalité de type tic-toc
est apparu lorsque j’ai essayé de profiler des calculs pilotés de manière interactive, c’est-à-dire par les mouvements de la souris de l’utilisateur dans une interface graphique. Je me sentais comme spammer tic
s et toc
s dans les sources tout en testant de manière interactive serait le moyen le plus rapide de révéler les goulots d'étranglement. J'y suis allé avec la classe Timer
d'Eli Bendersky, mais je n'étais pas totalement heureux, car cela me demandait de modifier l'indentation de mon code, ce qui peut être gênant pour certains éditeurs et perturber le système de contrôle de version. De plus, il peut être nécessaire de mesurer le temps entre les points de différentes fonctions, ce qui ne fonctionnerait pas avec l'instruction with
. Après avoir essayé beaucoup d'ingéniosité Python, voici la solution simple que j'ai trouvée qui fonctionnait le mieux:
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 repoussant les heures de début 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é à propos de la classe Timer
d'Eli.
Pour une raison quelconque, je me suis inquiété des frais généraux liés à une implémentation Python pure. J'ai donc également 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);
}
Ceci est pour MacOSX, et j'ai omis du code pour vérifier si lvl
est hors de portée pour des raisons de brièveté. Alors que tictoc.res()
donne une résolution d’environ 50 nanosecondes sur mon système, j’ai constaté que la gigue liée à la mesure d’une déclaration Python se situe facilement dans la plage des microsecondes (et beaucoup plus lorsqu’il est utilisé depuis 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 constaté que l'utilité de l'approche tic-toc
- est pratiquement limitée aux blocs de code dont l'exécution nécessite plus de 10 microsecondes. En dessous de cela, des stratégies de calcul de moyenne comme dans timeit
sont nécessaires pour obtenir une mesure fidèle.
Je viens de créer un module [tictoc.py] permettant de réaliser des tics imbriqués, comme le 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 ça marche comme ça:
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.
Juste au cas où quelqu'un serait intéressé. Sur la base de toutes les autres réponses, j’ai écrit un cours de tictoc qui a le meilleur de tous.
Le lien sur github est ici.
Vous pouvez également utiliser pip pour l'obtenir.
pip install ttictoc
En ce qui concerne son utilisation:
from ttictoc import TicToc
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
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 souhaitez 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)
La classe a 3 arguments: nom, méthode et indentation.
L'argument de 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.
Si version python> = 3.7: - time_ns ou 3: time.time_ns - perf_counter_ns ou 4: time.perf_counter_ns - process_time_ns ou 5: time.process_time_ns
Si vous préférez utiliser une autre méthode, utilisez simplement time.clock:
TicToc(method=time.clock)
Le cours est le suivant:
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
Regardez le timeit
module . Ce n'est pas vraiment équivalent, mais si le code que vous voulez utiliser est dans une fonction, vous pouvez facilement l'utiliser.
J'ai légèrement modifié la réponse de @Eli Bendersky: utiliser les fonctions ctor __init__()
et dtor __del__()
pour effectuer le chronométrage, afin de pouvoir l'utiliser plus facilement sans indenter le code d'origine:
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, il suffit de mettre Timer ("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
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 toute fonction et affiche le temps nécessaire à l'exécution de 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()
S'appuyant sur les réponses de Stefan et d'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
tic()
, toc()
et les imbriquer comme dans Matlabtic(1)
, toc(1)
ou tic('very-important-block')
, toc('very-important-block')
et les temporisateurs portant des noms différents n'interféreront(ici, toc n'imprime pas le temps écoulé, mais le renvoie.)
Mise à jour 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é en tant que 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 rapportées (secondes) et ajuster le nombre de chiffres comme suggéré par Can, et avec l'option d'ajouter également à un fichier journal. Vous devez importer date/heure pour utiliser la fonctionnalité de journalisation:
import time
import datetime
with Timer('Count', 'log.txt'):
for i in range(0,10_000_000):
pass