web-dev-qa-db-fra.com

Pourquoi boucler sur range () dans Python plus rapide que d'utiliser une boucle while?

L'autre jour, je faisais un Python benchmarking et je suis tombé sur quelque chose d'intéressant. Voici deux boucles qui font plus ou moins la même chose. La boucle 1 prend environ deux fois plus de temps que la boucle 2 pour s'exécuter .

Boucle 1:

int i = 0
while i < 100000000:
  i += 1

Boucle 2:

for n in range(0,100000000):
  pass

Pourquoi la première boucle est-elle tellement plus lente? Je sais que c'est un exemple trivial mais cela a piqué mon intérêt. Y a-t-il quelque chose de spécial dans la fonction range () qui la rend plus efficace que l'incrémentation d'une variable de la même manière?

69
A. Dorton

voir le démontage de python octet, vous pouvez avoir une idée plus concrète

utiliser la boucle while:

1           0 LOAD_CONST               0 (0)
            3 STORE_NAME               0 (i)

2           6 SETUP_LOOP              28 (to 37)
      >>    9 LOAD_NAME                0 (i)              # <-
           12 LOAD_CONST               1 (100000000)      # <-
           15 COMPARE_OP               0 (<)              # <-
           18 JUMP_IF_FALSE           14 (to 35)          # <-
           21 POP_TOP                                     # <-

3          22 LOAD_NAME                0 (i)              # <-
           25 LOAD_CONST               2 (1)              # <-
           28 INPLACE_ADD                                 # <-
           29 STORE_NAME               0 (i)              # <-
           32 JUMP_ABSOLUTE            9                  # <-
      >>   35 POP_TOP
           36 POP_BLOCK

Le corps de boucle a 10 op

gamme d'utilisation:

1           0 SETUP_LOOP              23 (to 26)
            3 LOAD_NAME                0 (range)
            6 LOAD_CONST               0 (0)
            9 LOAD_CONST               1 (100000000)
           12 CALL_FUNCTION            2
           15 GET_ITER
      >>   16 FOR_ITER                 6 (to 25)        # <-
           19 STORE_NAME               1 (n)            # <-

2          22 JUMP_ABSOLUTE           16                # <-
      >>   25 POP_BLOCK
      >>   26 LOAD_CONST               2 (None)
           29 RETURN_VALUE

Le corps de boucle a 3 op

Le temps d'exécution du code C est beaucoup plus court que celui de l'interpréteur et peut être ignoré.

142
kcwu

range() est implémentée en C, tandis que i += 1 est interprété.

L'utilisation de xrange() pourrait rendre la tâche encore plus rapide pour les grands nombres. Commencer par Python 3.0 range() est le même que précédemment xrange().

31
Georg Schölly

Il faut dire qu'il y a beaucoup de création et de destruction d'objets en cours avec la boucle while.

i += 1

est le même que:

i = i + 1

Mais parce que Python les entiers sont immuables, il ne modifie pas l'objet existant; il crée plutôt un nouvel objet avec une nouvelle valeur. Il s'agit essentiellement:

i = new int(i + 1)   # Using C++ or Java-ish syntax

Le garbage collector aura également une grande quantité de nettoyage à faire. "La création d'objets coûte cher".

12
Peter

Parce que vous utilisez plus souvent du code écrit en C dans l'interpréteur. c'est-à-dire que i + = 1 est en Python, donc lent (comparativement), tandis que la plage (0, ...) est un appel C, la boucle for s'exécutera principalement en C également.

3
John Montgomery

La plupart des appels de méthode intégrés de Python sont exécutés en tant que code C. Le code qui doit être interprété est beaucoup plus lent. En termes d'efficacité mémoire et de vitesse d'exécution, la différence est gigantesque. Les internes python ont été optimisés à l'extrême, et il est préférable de profiter de ces optimisations.

1
ohdeargod