Hvordan kan du profilere et Python-skript?

Project Euler og andre kodingskonkurranser har ofte en maksimal tid å kjøre, eller folk skryter av hvor raskt deres spesielle løsning kjører. Med python er tilnærmingene noen ganger noe klønete - dvs. å legge til timingkode til __main__.

Hva er en god måte å profilere hvor lang tid et python-program tar å kjøre?

Løsning

Python inkluderer en profiler kalt cProfile. Den gir ikke bare den totale kjøretiden, men også ganger hver funksjon separat, og forteller deg hvor mange ganger hver funksjon ble kalt, noe som gjør det enkelt å bestemme hvor du bør gjøre optimaliseringer.

Du kan kalle den fra koden din, eller fra tolkeren, slik som dette:

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

Enda mer nyttig kan du påkalle cProfile når du kjører et skript:

python -m cProfile myscript.py

For å gjøre det enda enklere har jeg laget en liten batch-fil som heter 'profile.bat':

python -m cProfile %1

Så alt jeg trenger å gjøre er å kjøre:

profile euler048.py

Og jeg får dette:

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: Oppdatert lenke til en god videoressurs fra PyCon 2013 med tittelen Python Profiling [Også via YouTube](https://www.youtube.com/watch?v=QJwVYlDzAXs).

Kommentarer (13)

Det er verdt å påpeke at bruk av profileren bare fungerer (som standard) på hovedtråden, og du vil ikke få informasjon fra andre tråder hvis du bruker dem. Dette kan være litt av en gotcha som det er helt ikke nevnt i profiler dokumentasjon.

Hvis du også ønsker å profilere tråder, vil du se på threading.setprofile()-funksjonen i dokumentasjonen.

Du kan også lage din egen threading.Thread underklasse for å gjøre det:

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,))

og bruke den ProfiledThread-klassen i stedet for standardklassen. Det kan gi deg mer fleksibilitet, men jeg er ikke sikker på at det er verdt det, spesielt hvis du bruker tredjepartskode som ikke bruker klassen din.

Kommentarer (6)

I Virtaals kilde er det en veldig nyttig klasse og dekoratør som kan gjøre profilering (selv for spesifikke metoder / funksjoner) veldig enkelt. Utgangen kan deretter vises veldig komfortabelt i KCacheGrind.

Kommentarer (1)