tic, fonctions toc analogiques en Python


Answers

J'ai eu la même question quand j'ai migré vers Python de Matlab. Avec l'aide de ce fil j'ai été capable de 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 tout! Nous sommes maintenant prêts à utiliser complètement 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 intégrées de Matlab. 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, en programmant 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 être capable de 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 commandes que vous voulez chronométrer, 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.

Question

Quel est le meilleur analogue des fonctions tic et toc de MATLAB ( http://www.mathworks.com/help/techdoc/ref/tic.html ) en Python?




Jetez un oeil sur le module timeit . Ce n'est pas vraiment équivalent, mais si le code que vous voulez programmer est à l'intérieur d'une fonction, vous pouvez facilement l'utiliser.




J'ai changé un peu la réponse de @Eli Bendersky pour utiliser le ctor __init__() et dtor __del__() pour faire le timing, afin qu'il puisse être utilisé plus facilement 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 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



Habituellement, %time , %timeit , %prun et %lprun (si line_profiler est installé) satisfont 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 pilotés de manière interactive, c'est-à-dire par le mouvement de la souris de l'utilisateur dans une interface graphique. J'ai eu envie de spammer les tic et les toc dans les sources alors que les tests interactifs seraient le moyen le plus rapide de révéler les goulots d'étranglement. Je suis allé avec la classe Timer Eli Bendersky, mais je n'étais pas entièrement content, car cela m'obligeait à 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'ingéniosité en Python, voici la solution simple que j'ai trouvée la plus efficace:

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 et de toc . Cela permet aussi de changer la chaîne de format de l'instruction toc pour afficher des informations supplémentaires, ce que j'ai aimé dans la classe Timer d'Eli.

Pour une raison quelconque, je m'inquiétais de la surcharge d'une implémentation pure de Python, aussi j'ai 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 si lvl est hors limites pour la brièveté. 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 microseconde (et beaucoup plus lorsqu'il est utilisé à partir de 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-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 calcul de la moyenne comme dans le timeit sont nécessaires pour obtenir une mesure fidèle.




Mise à jour de la réponse d' Eli à Python 3:

class Timer(object):
    def __init__(self, name=None):
        self.name = name

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        message = 'Elapsed: %s' % (time.time() - self.tstart)
        if self.name:
            message = '[%s] ' % self.name + message
        print(message)



Links