[PYTHON] Introduction à l'optimisation

Voyons comment utiliser un profileur en Python pour chronométrer et accélérer un programme.

Qu'est-ce que l'optimisation?

Qu'est-ce que l'optimisation de programme?

Cette fois, nous nous concentrerons sur la réduction du temps de traitement

Ce qui prend du temps

Mesurons le temps de traitement du programme.

En supposant que vous ayez deux programmes qui prennent 5 secondes, essayez de mesurer avec la commande de temps.

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

Dans les deux cas, il faut un total de 5 secondes du début à la fin du programme, mais la situation est légèrement différente.

Jetons un coup d'œil à la source réellement utilisée:

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

Comment utiliser le temps est différent

En général, les programmes passent leur temps à faire des choses comme:

Améliorez la façon dont vous passez votre temps

Trouvez un goulot d'étranglement

Où pourriez-vous vous améliorer dans le programme?

Comment trouver

Journal

Sortie journal du décalage horaire avant et après traitement

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

Un des outils appelé "Profiler" fourni avec Python.

Depuis la ligne de commande

Exécuter avec un programme à exécuter comme la commande time en argument

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}
article Signification de la valeur
ncalls Nombre d'appels
tottime Temps passé(N'inclut pas ce qui a été appelé)
percall Temps passé par appel(tottime/ncalls)
cumtime Temps passé(Y compris ce que vous avez appelé)
percall Temps passé par appel(cumtime/ncalls)

Lorsque vous exécutez cProfile.py à partir de la ligne de commande

Incorporer dans le code

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}

Comment procéder à l'optimisation

Améliorer les exigences non fonctionnelles tout en maintenant les exigences fonctionnelles

Comment recommander

essayons

Séquence de Fibonacci

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}

Dans cet exemple, fib est appelé trop souvent. (M / N de tottime signifie M appels récursifs et N appels non récursifs)

Notez également que ce dernier prend plus de temps lorsque l'on compare le temps d'exécution sans profil (0,540s) mesuré avec la commande de temps et le temps d'exécution avec profil (1,084s).

Améliorez les performances tout en maintenant la fonctionnalité

Essayez en fait

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}

Le nombre d'appels a été réduit et le temps global a été raccourci!

Le deuxième exemple. Une fonction qui prend la somme du début à la fin est implémentée à l'aide de la fonction standard sum.

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}

Réfléchissons à la façon dont nous pouvons réduire le temps de traitement.

Résumé

Recommended Posts

Introduction à l'optimisation
Premiers pas avec Android!
1.1 Premiers pas avec Python
Premiers pas avec apache2
Premiers pas avec Python
Premiers pas avec Numpy
Premiers pas avec Spark
Premiers pas avec Python
Premiers pas avec Pydantic
Premiers pas avec Jython
Premiers pas avec Django 2
Traduire Premiers pas avec TensorFlow
Introduction aux fonctions Python
Introduction à Tkinter 2: Button
Premiers pas avec Go Assembly
Premiers pas avec PKI avec Golang ―― 4
Premiers pas avec Python Django (1)
Premiers pas avec Python Django (4)
Premiers pas avec Python Django (3)
Introduction à Python Django (6)
Premiers pas avec Django avec PyCharm
Premiers pas avec Python Django (5)
Premiers pas avec Python responder v2
Introduction à Git (1) Stockage d'historique
Premiers pas avec les applications Web Python
Premiers pas avec Python pour les classes PHPer
Premiers pas avec Sparse Matrix avec scipy.sparse
Premiers pas avec Julia pour Pythonista
Premiers pas avec Python Bases de Python
Premiers pas avec Cisco Spark REST-API
Commençant par USD sur Windows
Premiers pas avec les algorithmes génétiques Python
Premiers pas avec Python 3.8 sous Windows
Premiers pas avec Python pour les fonctions PHPer
Premiers pas avec CPU Steal Time
Grails pour commencer
Premiers pas avec python3 # 1 Apprenez les connaissances de base
Premiers pas avec Python Web Scraping Practice
Premiers pas avec Python pour PHPer-Super Basics
Premiers pas avec Python Web Scraping Practice
Premiers pas avec Dynamo de Python boto
Premiers pas avec Lisp pour Pythonista: Supplément
Premiers pas avec Heroku, déploiement de l'application Flask
Premiers pas avec TDD avec Cyber-dojo chez MobPro
Démarrer avec Python avec 100 coups sur le traitement du langage
Django 1.11 a démarré avec Python3.6
Principes de base de MongoDB: Premiers pas avec CRUD avec JAVA
Premiers pas avec la traduction japonaise du modèle séquentiel Keras
[Français] Premiers pas avec Rust pour les programmeurs Python
Aménagement routier par optimisation
Django Getting Started Part 2 avec eclipse Plugin (PyDev)
Démarrez avec MicroPython
Premiers pas avec AWS IoT facilement en Python
Démarrez avec Mezzanine
Premiers pas avec le module ast de Python (à l'aide de NodeVisitor)