web-dev-qa-db-fra.com

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? 

73
Alex

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.

124
Eli Bendersky

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.

21
Benben

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()
15
GuestPoster

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 tics et tocs 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 tics et tocs. 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. 

11
Stefan

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.

5
antonimmo

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:

Importez-le

from ttictoc import TicToc

Utiliser la déclaration 'avec'

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

Appeler tic toc 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 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)

Arguments

La classe a 3 arguments: nom, méthode et indentation.

  • name: c'est le nom de l'objet. Ce n'est pas obligatoire.
  • méthode: Indique quelle méthode doit être utilisée pour obtenir l'heure.
  • indentation: Permet d'utiliser le même objet plusieurs fois, avec différentes indentations dans le temps.

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.

  • heure ou 0: heure
  • perf_counter ou 1: time.perf_counter
  • process_time ou 2: time.process_time

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
4
H. Sánchez

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.

3
ThiefMaster

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
1
Shaohua Li

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()
1
Coen Jonker

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

  • 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 temporisateurs portant des noms différents n'interféreront
  • leur importation de cette manière évite les interférences entre les modules qui l'utilisent.

(ici, toc n'imprime pas le temps écoulé, mais le renvoie.)

0
Maxim

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
0
Josiah Yoder