Mal sehen, wie man einen Profiler in Python verwendet, um ein Programm zeitlich zu steuern und zu beschleunigen.
Was ist Programmoptimierung?
Dieses Mal konzentrieren wir uns auf die Verkürzung der Bearbeitungszeit
Lassen Sie uns die Verarbeitungszeit des Programms messen.
Angenommen, Sie haben zwei Programme, die 5 Sekunden dauern, versuchen Sie, mit dem Befehl time zu messen.
python
% time ./wait.py
./wait.py 0.02s user 0.02s system 0% cpu 5.057 total
% time ./busy.py
./busy.py 5.01s user 0.02s system 99% cpu 5.038 total
In beiden Fällen dauert es vom Beginn bis zum Ende des Programms insgesamt 5 Sekunden, aber die Situation ist etwas anders.
Werfen wir einen Blick auf die tatsächlich verwendete Quelle:
wait.py
#!/usr/bin/env python
import time
def main():
time.sleep(5)
if __name__ == '__main__':
main()
busy.py
#!/usr/bin/env python
import time
def main():
start = time.time()
while time.time() - start < 5:
pass
if __name__ == '__main__':
main()
Der Umgang mit Zeit ist anders
Im Allgemeinen verbringen Programme ihre Zeit mit Dingen wie:
Verbessern Sie, wie Sie Ihre Zeit verbringen
Wo im Programm könnten Sie sich verbessern?
Wie findet man
Protokollausgabe der Zeitdifferenz vor und nach der Verarbeitung
python
start = time.time()
some_func()
print "%f sec" % (time.time() - start)
python
start = time.clock()
some_func()
print "%f sec" % (time.clock() - start)
cProfile
Eines der Tools namens "Profiler", das mit Python geliefert wird.
Mit einem Programm ausführen, das wie der Befehl time als Argument ausgeführt werden soll
python
% python -m cProfile wait.py
4 function calls in 5.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.001 0.001 5.002 5.002 wait.py:3(<module>)
1 0.000 0.000 5.001 5.001 wait.py:5(main)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 5.001 5.001 5.001 5.001 {time.sleep}
Artikel | Bedeutung des Wertes |
---|---|
ncalls | Anzahl der Anrufe |
tottime | Zeitaufwand(Beinhaltet nicht, was genannt wurde) |
percall | Zeitaufwand pro Anruf(tottime/ncalls) |
cumtime | Zeitaufwand(Einschließlich dessen, was Sie angerufen haben) |
percall | Zeitaufwand pro Anruf(cumtime/ncalls) |
Wenn Sie cProfile.py über die Befehlszeile ausführen
wait_profile.py
#!/usr/bin/env python
import cProfile
import time
def main():
time.sleep(5)
if __name__ == '__main__':
cProfile.run("main()", "wait.prof")
% python -c "import pstats; pstats.Stats('wait.prof').strip_dirs().sort_stats(-1).print_stats()"
Fri Jun 17 00:25:58 2016 wait.prof
4 function calls in 5.005 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 5.005 5.005 <string>:1(<module>)
1 0.000 0.000 5.005 5.005 wait_profile.py:6(main)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 5.005 5.005 5.005 5.005 {time.sleep}
Verbessern Sie nicht funktionale Anforderungen, während Sie die funktionalen Anforderungen beibehalten
Wie zu empfehlen
Fibonacci-Folge
fib.py
#!/usr/bin/env python
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-2) + fib(n-1)
if __name__ == '__main__':
assert fib(30) == 832040
python
% time ./fib.py
python fib.py 0.52s user 0.01s system 98% cpu 0.540 total
% python -m cProfile fib.py
2692539 function calls (3 primitive calls) in 1.084 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.084 1.084 fib.py:3(<module>)
2692537/1 1.084 0.000 1.084 1.084 fib.py:3(fib)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
In diesem Beispiel wird fib zu oft aufgerufen. (M / N Tottime bedeutet M rekursive Aufrufe und N nicht rekursive Aufrufe)
Beachten Sie auch, dass letzteres länger dauert, wenn die mit dem Zeitbefehl gemessene Ausführungszeit ohne Profil (0,540 s) und die Ausführungszeit mit Profil (1,084 s) verglichen werden.
Verbessern Sie die Leistung bei gleichzeitiger Beibehaltung der Funktionalität
Eigentlich versuchen
fib_optimized.py
#!/usr/bin/env python
cache = {}
def fib(n):
if n in cache:
return cache[n]
if n == 0:
cache[n] = 0
elif n == 1:
cache[n] = 1
else:
cache[n] = fib(n-2) + fib(n-1)
return cache[n]
if __name__ == '__main__':
assert fib(30) == 832040
python
% python -m cProfile fib_optimized.py
61 function calls (3 primitive calls) in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 fib_optimized.py:3(<module>)
59/1 0.000 0.000 0.000 0.000 fib_optimized.py:5(fib)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Die Anzahl der Anrufe wurde reduziert und die Gesamtzeit verkürzt!
Das zweite Beispiel. Eine Funktion, die die Summe von Anfang bis Ende nimmt, wird unter Verwendung der Standardfunktionssumme implementiert.
takesum.py
#!/usr/bin/env python
def takesum(beg, end):
"take sum of beg, beg+1, ..., end"
assert beg <= end
i = beg
xs = []
while i <= end:
xs.append(i)
i += 1
return sum(xs)
if __name__ == '__main__':
assert takesum(0, 10000000) == 50000005000000
python
% python -m cProfile takesum.py
10000005 function calls in 3.482 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.076 0.076 3.482 3.482 takesum.py:3(<module>)
1 2.418 2.418 3.405 3.405 takesum.py:3(takesum)
10000001 0.878 0.000 0.878 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.109 0.109 0.109 0.109 {sum}
Überlegen Sie, wie Sie die Verarbeitungszeit reduzieren können.
Recommended Posts