Vitesse de notation d'inclusion de liste en Python

La notation d'inclusion de liste est-elle rapide?

Dans Python 2.6.6, nous avons comparé les temps d'exécution des méthodes 1,2,3 suivantes pour ajouter 10 000 000 d'entiers. En conclusion, la notation d'inclusion de liste de 3 était aussi rapide que prévu. Comme je l'ai écrit dans le post-scriptum, le contenu décrit ici peut également être appliqué aux séries Python 2.7 et 3.3.

  1. testfunc1: Préparez une liste vide et ajoutez
  2. testfunc2: 1 + append est transformé en objet
  3. testfunc3: Notation d'inclusion de liste

Préparation

# 1. testfunc1:Préparez une liste vide et ajoutez
def testfunc1(rangelist):
    templist = []
    for temp in rangelist:
        templist.append(temp)

# 2. testfunc2: 1+Objectify append
def testfunc2(rangelist):
    templist = []
    append = templist.append
    for temp in rangelist:
        append(temp)

# 3. testfunc3:Notation d'inclusion de liste
def testfunc3(rangelist):
    templist = [temp for temp in rangelist]

La mesure

Utilisez la commande% timeit d'IPython pour exécuter 10 fois "Extraire le plus rapide des 3 exécutions" pour trouver le temps d'exécution moyen.

# 10,000,Créer une liste de 000 entiers
>>> rangelist = range(1,10000000)
>>> %timeit -n 10 -r 3 testfunc1(rangelist)
10 loops, best of 3: 1.73 s per loop
>>> %timeit -n 10 -r 3 testfunc2(rangelist)
10 loops, best of 3: 1.08 s per loop
>>> %timeit -n 10 -r 3 testfunc3(rangelist)
10 loops, best of 3: 697 ms per loop

résultat

  1. 1.73 s
  2. 1.08 s
  3. 697 ms

Conclusion

** Dans une boucle où le temps d'exécution est un problème, il est préférable d'utiliser une notation d'inclusion de liste telle que 3. ** ** ** Même si vous n'utilisez pas la notation d'inclusion de liste, il est préférable de mettre la référence de la méthode append dans la variable à l'avance comme dans 2. ** **

Notez que la deuxième méthode consistant à placer la référence de méthode dans une variable à l'avance peut également être utilisée pour raccourcir le temps d'exécution de la boucle autre que la création de liste.


Démonter et confirmer la raison

Examinons pourquoi ce résultat est obtenu en désassemblant chaque fonction à l'aide du module dis de Python.

Démonter

>>> import dis
>>> dis.dis(testfunc1)
  3           0 BUILD_LIST               0
              3 STORE_FAST               1 (templist)

  4           6 SETUP_LOOP              27 (to 36)
              9 LOAD_FAST                0 (rangelist)
             12 GET_ITER            
        >>   13 FOR_ITER                19 (to 35)
             16 STORE_FAST               2 (temp)

  5          19 LOAD_FAST                1 (templist)
             22 LOAD_ATTR                0 (append)
             25 LOAD_FAST                2 (temp)
             28 CALL_FUNCTION            1
             31 POP_TOP             
             32 JUMP_ABSOLUTE           13
        >>   35 POP_BLOCK           
        >>   36 LOAD_CONST               0 (None)
             39 RETURN_VALUE        

>>> dis.dis(testfunc2)
  9           0 BUILD_LIST               0
              3 STORE_FAST               1 (templist)

 10           6 LOAD_FAST                1 (templist)
              9 LOAD_ATTR                0 (append)
             12 STORE_FAST               2 (append)

 11          15 SETUP_LOOP              24 (to 42)
             18 LOAD_FAST                0 (rangelist)
             21 GET_ITER            
        >>   22 FOR_ITER                16 (to 41)
             25 STORE_FAST               3 (temp)

 12          28 LOAD_FAST                2 (append)
             31 LOAD_FAST                3 (temp)
             34 CALL_FUNCTION            1
             37 POP_TOP             
             38 JUMP_ABSOLUTE           22
        >>   41 POP_BLOCK           
        >>   42 LOAD_CONST               0 (None)
             45 RETURN_VALUE        

>>> dis.dis(testfunc3)
 16           0 BUILD_LIST               0
              3 DUP_TOP             
              4 STORE_FAST               1 (_[1])
              7 LOAD_FAST                0 (rangelist)
             10 GET_ITER            
        >>   11 FOR_ITER                13 (to 27)
             14 STORE_FAST               2 (temp)
             17 LOAD_FAST                1 (_[1])
             20 LOAD_FAST                2 (temp)
             23 LIST_APPEND         
             24 JUMP_ABSOLUTE           11
        >>   27 DELETE_FAST              1 (_[1])
             30 STORE_FAST               3 (templist)
             33 LOAD_CONST               0 (None)
             36 RETURN_VALUE        

Commentaire

De la sortie ci-dessus, FOR_ITER à JUMP_ABSOLUTE est une boucle appelée 10 000 000 fois.

Comparaison de testfunc1 et testfunc2

Dans testfunc1, vous pouvez voir que 22 LOAD_ATTR appelle la méthode append de l'objet de liste, puis 28 CALL_FUNCTION ajoute réellement.

testfunc1 pour la boucle


        >>   13 FOR_ITER                19 (to 35)
             16 STORE_FAST               2 (temp)

  5          19 LOAD_FAST                1 (templist)
             22 LOAD_ATTR                0 (append)
             25 LOAD_FAST                2 (temp)
             28 CALL_FUNCTION            1
             31 POP_TOP             
             32 JUMP_ABSOLUTE           13

D'autre part, dans testfunc2, LOAD_ATTR est hors de la boucle, et à l'intérieur de la boucle, 28 LOAD_FAST appelle la méthode append, puis 34 CALL_FUNCTION exécute append.

testfunc2 pour la boucle


        >>   22 FOR_ITER                16 (to 41)
             25 STORE_FAST               3 (temp)

 12          28 LOAD_FAST                2 (append)
             31 LOAD_FAST                3 (temp)
             34 CALL_FUNCTION            1
             37 POP_TOP             
             38 JUMP_ABSOLUTE           22

LOAD_ATTR et LOAD_FAST prennent plus de temps que LOAD_ATTR. Par conséquent, testfunc2 avec LOAD_ATTR en dehors de la boucle a un temps d'exécution plus court que testfunc1 avec LOAD_ATTR à l'intérieur de la boucle.

Comparaison de testfunc2 et testfunc3

Dans testfunc2, j'ai chargé la méthode append avec 28 LOAD_FAST, puis je l'ai appelée avec 34 CALL_FUNCTION. D'autre part, testfunc3 appelle 23 LIST_APPEND, un processus de liste uniquement, et il n'y a pas de CALL_FUNCTION.

testfunc3 pour la boucle


        >>   11 FOR_ITER                13 (to 27)
             14 STORE_FAST               2 (temp)
             17 LOAD_FAST                1 (_[1])
             20 LOAD_FAST                2 (temp)
             23 LIST_APPEND         
             24 JUMP_ABSOLUTE           11

Pour CALL_FUNCTION et LIST_APPEND, l'appel de LIST_APPEND, qui est un processus de liste uniquement, est plus rapide (CALL_FUNCTION a différents processus car il s'agit d'un appel de fonction général). Par conséquent, testfunc3 avec CALL_FUNCTION remplacé par LIST_APPEND a un temps d'exécution plus court que testfunc2 avec CALL_FUNCTION dans la boucle.

référence

Cet article a été rédigé en référence aux articles suivants.


Postscript

J'ai également comparé Python 2.7 et 3.3 pour m'assurer que ce qui précède ne concerne pas uniquement Python 2.6. Depuis que j'ai changé la machine, il ne sert à rien de la comparer avec le temps d'exécution ci-dessus.

résultat

Python 2.7.4

>>> %timeit -n 5 testfunc1()
5 loops, best of 3: 974 ms per loop
>>> %timeit -n 5 testfunc2()
5 loops, best of 3: 737 ms per loop
>>> %timeit -n 5 testfunc3()
5 loops, best of 3: 430 ms per loop

Python 3.3.3

>>> %timeit -n 5 testfunc1()
5 loops, best of 3: 1.21 s per loop
>>> %timeit -n 5 testfunc2()
5 loops, best of 3: 815 ms per loop
>>> %timeit -n 5 testfunc3()
5 loops, best of 3: 639 ms per loop

Eh bien, Python3 est plus lent ... Au fait, dans Python3, il semble que la fonction range renvoie un itérateur au lieu d'une liste, donc créons et exécutons une liste sans utiliser la fonction range.

Python 3.3.3 + fonction de plage inutilisée

>>> %timeit -n 5 testfunc1()
5 loops, best of 3: 1.19 s per loop
>>> %timeit -n 5 testfunc2()
5 loops, best of 3: 611 ms per loop
>>> %timeit -n 5 testfunc3()
5 loops, best of 3: 429 ms per loop

Je me méfie toujours de testfunc1, mais maintenant je peux exécuter testfunc2 et testfunc3 dans à peu près le même laps de temps que Python 2.7.4.

Conclusion

La même chose peut être dite pour Python 2.7 et Python 3.3 pour 1, 2 et 3 comme pour Python 2.6.

Recommended Posts

Vitesse de notation d'inclusion de liste en Python
Python> Compréhension / Notation inclusive> Compréhension de liste
Exercice Python 2 - Notation d'inclusion de liste
Notation d'inclusion Python
Notation d'inclusion Python
Notation d'inclusion de liste
[Python] liste
À propos de la notation d'inclusion de python
bases de python: liste
Remarque: Notation d'inclusion de liste
Manipulation de liste Python
Notation inclusive de Python (à propos de l'expression de liste et de générateur) [supplémentaire]
Liste triée en Python
[Python] Compréhension de liste Différentes façons de créer une liste
Liste des modules python
Python> liste> extend () ou + =
Liste triée en Python
FizzBuzz en notation d'inclusion de liste
Python ~ Apprentissage rapide de la grammaire ~
[Introduction à Udemy Python3 + Application] 60. Notation d'inclusion de liste
Liste de filtres en Python
liste assertXXX unittest python
Mémo de type Liste / Dictionnaire Python3
[Mémo] Tri de liste Python3
Liste des API Python pour OpenCV3
Liste des erreurs Python (japonais)
Notation inclusive, pas seulement une liste
La chose semblable à une recherche de liste en Python
J'ai mesuré la vitesse de la notation d'inclusion de liste, pendant et pendant avec python2.7.
Liste des classes d'exception Python
Tri rapide 2 | Facile avec la notation d'inclusion de liste
Initialiser la liste avec python
compréhension de liste car operator.methodcaller ne peut pas être utilisé avec python 2.5
Notation d'inclusion Python3 (liste, dictionnaire) que j'ai vue quelque part
Jeu de main Python (liste à deux dimensions)
python3 Mesurez la vitesse de traitement.
Liste Python, pour instruction, dictionnaire
Résumé des opérations de liste Python3
Comparaison de vitesse de Python, Java, C ++
[Python] Convertir la liste en Pandas [Pandas]
Création de liste de tâches [Python Django]
Spécifier plusieurs index de liste (Python)
Cours de base Python (5 List Taple)
Mesurer la vitesse WiFi avec Python
La liste Python n'est pas une liste
J'ai comparé la vitesse de la référence du python dans la liste et la référence de l'inclusion du dictionnaire faite à partir de la liste dans.
[Python] Copie d'une liste multidimensionnelle
[Introduction à Python] <liste> [modifier le 22/02/2020]
Liste Python et tapples et virgules
Paiza Python Primer 4: Notions de base sur les listes
Notation et générateur d'inclusion de liste Python
[Python / PyQ] 4. liste, pour instruction
#List Python pour les super débutants
Obtenir des éléments de liste en Python
[Route vers Python intermédiaire] Utiliser l'instruction if dans la notation d'inclusion de liste
Extraire plusieurs doublons de liste en Python
Différence entre list () et [] en Python
[python] Gérer les fonctions dans une liste
Sortie de la liste du vendredi Premium 2017 en Python