Quel est l'équivalent Python des fonctions tic et toc de Matlab?


113

Quel est l'équivalent Python des fonctions tic et toc de Matlab ?


8
Si vous voulez vraiment l'équivalent direct, appelez simplement tic = time.time()et toc = time.time(), alors print toc-tic, 'sec Elapsed'comme les gens l'ont dit ci-dessous, cependant, timeitc'est plus robuste.
Joe Kington

Il me semble obtenir de meilleurs résultats en utilisant l'approche de @ JoeKington en conjonction avec timeit.default_timer (), comme ceci par exemple:, tic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer()alors print toc-tic.
littleO

1
La bibliothèque pytictoc semble la plus conveinente, la syntaxe est même légèrement plus claire que ttictoc ci-dessous. pypi.org/project/pytictoc
FlorianH

Réponses:


174

Mis à part timeitce 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.


25
@eat: Je suis respectueusement en désaccord. Les gens utilisent depuis toujours la timecommande unix pour mesurer les temps d'exécution des programmes, et cette méthode reproduit cela dans le code Python. Je ne vois rien de mal à cela, tant que c'est le bon outil pour le travail. timeitn'est pas toujours cela, et un profileur est une solution beaucoup plus lourde pour la plupart des besoins
Eli Bendersky

4
Pour la dernière ligne, je suggérerais print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'. C'est difficile à comprendre sans le% .2f. Merci pour cette excellente idée.
Can Kavaklıoğlu

4
Cela semble très pratique à première vue, mais en pratique, il faut indenter le bloc de code que l'on veut chronométrer, ce qui peut être assez gênant selon la longueur du bloc de code et l'éditeur de choix. Encore une solution élégante, qui se comporte correctement en cas d'utilisation imbriquée.
Stefan

1
Je pense que tu veux elapsed = t - time.time(), au lieu de elapsed = time.time() - t. Dans ce dernier écoulé sera négatif. J'ai suggéré ce changement en tant que modification.
rysqui

3
@rysqui - L' heure actuelle n'est-elle pas toujours un nombre plus grand qu'une heure précédente ? Je pense que elapsed = time.time() - tc'est la forme qui donne toujours une valeur positive.
Scott Smith

32

J'ai eu la même question lorsque j'ai migré vers python depuis Matlab. Avec l'aide de ce fil, j'ai pu construire un analogue exact du Matlab tic()et des toc()fonctions. 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 à être pleinement utilisés 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 TicTocGeneratorpour suivre plusieurs opérations ou simplement pour chronométrer les choses différemment. Par exemple, tout en chronométrant un script, nous pouvons désormais chronométrer chaque élément 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 de l'utiliser à tic()chaque fois. Si vous avez une série de commandes que vous souhaitez chronométrer, 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.


22

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"

Ensuite, vous pouvez les utiliser comme:

tic()
# do stuff
toc()

6
Cela ne fonctionnera pas correctement dans le cas d'une utilisation imbriquée de ticet toc, que Matlab prend en charge. Un peu plus de sophistication serait nécessaire.
Stefan

2
J'ai implémenté des fonctions similaires dans mon propre code lorsque j'avais besoin d'un timing de base. Je supprimerais cependant l' import timeextérieur des deux fonctions, car cela peut prendre un certain temps.
Bas Swinckels

Si vous insistez pour utiliser cette technique et que vous en avez besoin pour gérer les tic / toc imbriqués, créez une liste globale et laissez-la ticpousser et tocsortez d'elle.
Ahmed Fasih

1
J'ai aussi lu ailleurs que timeit.default_timer()c'est mieux que time.time()parce que cela time.clock()pourrait être plus approprié selon le système d'exploitation
Miguel

@AhmedFasih C'est ce que fait ma réponse, même si d'autres choses pourraient être améliorées.
antonimmo le

15

Habituellement, IPython de %time, %timeit, %prunet %lprun(si l' on a line_profilerinstallé) satisfaire mes besoins de profilage très bien. Cependant, un cas d'utilisation pour une tic-tocfonctionnalité similaire est apparu lorsque j'ai essayé de profiler des calculs qui étaient pilotés de manière interactive, c'est-à-dire par le mouvement de la souris de l'utilisateur dans une interface graphique. J'avais l'impression que spammer les 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. Je suis allé avec la Timerclasse d' Eli Bendersky , mais je n'étais pas entièrement satisfait, car cela m'obligeait à changer l'indentation de mon code, ce qui peut être gênant pour 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 lewithdéclaration. Après avoir essayé beaucoup d'intelligence Python, voici la solution simple que j'ai trouvée fonctionnant 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 poussant les heures de départ sur une pile, cela fonctionnera correctement pour plusieurs niveaux de tics et de tocs. Cela permet également de changer la chaîne de format de l' tocinstruction pour afficher des informations supplémentaires, ce que j'ai aimé sur la Timerclasse d'Eli .

Pour une raison quelconque, je me suis préoccupé de la surcharge d'une implémentation pure Python, alors j'ai é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);
}

C'est pour MacOSX, et j'ai omis le code pour vérifier s'il lvlest hors limites par souci de concision. Bien que tictoc.res()donne une résolution d'environ 50 nanosecondes sur mon système, j'ai trouvé que l'instabilité de la mesure de toute instruction Python se situe facilement dans la plage de la microseconde (et bien plus encore lorsqu'elle est utilisée à 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' tic-tocapproche -approach est pratiquement limitée aux blocs de code qui prennent plus de 10 microsecondes à exécuter. En dessous, des stratégies de calcul de la moyenne comme dans timeitsont nécessaires pour obtenir une mesure fidèle.


1
Extrêmement élégant, @Stefan - je ne peux pas croire que ce soit si bas. Merci!
thclark

10

Vous pouvez utiliser ticet à tocpartir de ttictoc. Installez-le avec

pip install ttictoc

Et importez-les simplement dans votre script comme suit

from ttictoc import tic,toc
tic()
# Some code
print(toc())

8

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 cela aide.


Belle réplique de tic / toc de MATLAB!
Matt

1
Je dois vous avertir que cela pourrait ne pas se comporter comme vous le souhaitez lorsqu'il est utilisé simultanément par plus d'un module, car les modules (AFAIK) se comportent comme des singletons.
antonimmo le

3

Jetez un œil au timeitmodule. Ce n'est pas vraiment équivalent mais si le code que vous voulez chronométrer est à l'intérieur d'une fonction, vous pouvez facilement l'utiliser.


Oui, timeitc'est mieux pour les benchmarks. Il n'est même pas nécessaire que ce soit une fonction unique, vous pouvez passer des instructions abritement complexes.

10
Eh bien, passer du code qui n'est pas un appel de fonction extrêmement simple sous forme de chaîne est très moche.
ThiefMaster


1

Cela peut également être fait à l'aide d'un wrapper. Façon très générale de garder le temps.

L'encapsuleur de cet exemple de code encapsule n'importe quelle fonction et imprime 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()

La fonction wrapper, timthis est appelée un décorateur. Une explication un peu plus détaillée, ici: medium.com/pythonhive
Mircea

1

J'ai changé un peu 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 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 l'utiliser, mettez simplement 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

3
Un problème avec cette implémentation est le fait qu'elle timern'est pas supprimée après le dernier appel, si un autre code suit après la forboucle. Pour obtenir la dernière valeur du timer, il faut supprimer ou écraser timerla forboucle après la boucle, par exemple via timer = None.
bastelflp

1
@bastelflp Je viens de réaliser que j'ai mal compris ce que vous vouliez dire ... Votre suggestion a été incorporée dans le code maintenant. Merci.
Shaohua Li

1

Mise à jour de 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

Production:

[Count] Elapsed: 0.27 seconds

Je l'ai également mis à jour pour imprimer les unités de temps rapportées (secondes) et couper 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

En m'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 utils.pymodule, 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 les nommer: tic(1), toc(1)ou tic('very-important-block'), toc('very-important-block')et minuteries avec des noms différents n'interférer
  • les importer de cette façon empêche les interférences entre les modules qui l'utilisent.

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.