Come si profila uno script Python?

Il Progetto Euler e altre gare di codifica spesso hanno un tempo massimo di esecuzione o le persone si vantano di quanto veloce sia la loro particolare soluzione. Con python, a volte gli approcci sono un po' macchinosi - per esempio, aggiungendo codice di temporizzazione a __main__.

Qual è un buon modo per valutare quanto tempo impiega un programma python ad essere eseguito?

Soluzione

Python include un profiler chiamato cProfile. Non solo dà il tempo di esecuzione totale, ma cronometra anche ogni funzione separatamente, e vi dice quante volte ogni funzione è stata chiamata, rendendo facile determinare dove dovreste fare ottimizzazioni.

Potete chiamarlo dall'interno del vostro codice, o dall'interprete, in questo modo:

import cProfile
cProfile.run('foo()')

Ancora più utile, si può invocare il cProfile quando si esegue uno script:

python -m cProfile myscript.py

Per renderlo ancora più facile, ho fatto un piccolo file batch chiamato 'profile.bat':

python -m cProfile %1

Quindi tutto quello che devo fare è eseguire:

profile euler048.py

E ottengo questo:

{{{55564}}};

1007 function calls in 0.061 CPU seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.061    0.061 :1()
 1000    0.051    0.000    0.051    0.000 euler048.py:2()
    1    0.005    0.005    0.061    0.061 euler048.py:2()
    1    0.000    0.000    0.061    0.061 {execfile}
    1    0.002    0.002    0.053    0.053 {map}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler objects}
    1    0.000    0.000    0.000    0.000 {range}
    1    0.003    0.003    0.003    0.003 {sum}

EDIT: Aggiornato il link ad una buona risorsa video da PyCon 2013 intitolata Python Profiling [Anche via YouTube](https://www.youtube.com/watch?v=QJwVYlDzAXs).

Commentari (13)

Vale la pena sottolineare che l'uso del profilatore funziona solo (per impostazione predefinita) sul thread principale, e non si otterrà alcuna informazione da altri thread se li si utilizza. Questo può essere un po' un intoppo in quanto è completamente non menzionato nella documentazione del profilatore.

Se volete anche profilare i thread, dovrete dare un'occhiata alla funzione threading.setprofile() nella documentazione.

Potresti anche creare la tua sottoclasse threading.Thread per farlo:

class ProfiledThread(threading.Thread):
    # Overrides threading.Thread.run()
    def run(self):
        profiler = cProfile.Profile()
        try:
            return profiler.runcall(threading.Thread.run, self)
        finally:
            profiler.dump_stats('myprofile-%d.profile' % (self.ident,))

e usare quella classe ProfiledThread invece di quella standard. Potrebbe darti più flessibilità, ma non sono sicuro che ne valga la pena, specialmente se stai usando codice di terze parti che non userebbe la tua classe.

Commentari (6)

In Virtaal source c'è una classe e un decoratore molto utili che possono rendere il profiling (anche per specifici metodi/funzioni) molto facile. L'output può poi essere visualizzato molto comodamente in KCacheGrind.

Commentari (1)