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?

68
demandé sur Alex 2011-05-01 20:46:34

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.

113
répondu Eli Bendersky 2011-05-01 16:55:13

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.

20
répondu Benben 2014-11-02 02:44:46

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()
14
répondu GuestPoster 2013-09-19 19:12:49

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.

10
répondu Stefan 2013-11-15 17:07:09

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.

5
répondu antonimmo 2017-05-18 18:52:15

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.

3
répondu ThiefMaster 2011-05-01 16:48:08

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
3
répondu H. Sánchez 2018-07-27 18:53:23

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()
1
répondu Coen Jonker 2016-07-27 13:30:33

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
1
répondu Shaohua Li 2017-08-30 05:37:35

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) ou tic('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.)

0
répondu Maxim 2017-10-22 13:30:36

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
0
répondu Josiah Yoder 2018-07-26 21:04:54