2009-05-15 11 views
56

Neulich habe ich ein Python-Benchmarking gemacht und bin auf etwas Interessantes gestoßen. Unten sind zwei Schleifen, die mehr oder weniger die gleiche Sache machen. Schleife 1 dauert etwa doppelt so lange wie Schleife 2 zur Ausführung.Warum ist das Schleifen von range() in Python schneller als mit einer while-Schleife?

Loop 1:

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

Loop 2:

for n in range(0,100000000): 
    pass 

Warum ist die erste Schleife so viel langsamer? Ich weiß, es ist ein triviales Beispiel, aber es hat mein Interesse geweckt. Gibt es etwas Besonderes an der Funktion range(), die es effizienter macht, als eine Variable auf die gleiche Weise zu inkrementieren?

Antwort

113

die Zerlegung des Python-Bytecode, können Sie eine konkretere Vorstellung

Verwendung erhalten while-Schleife:

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 

Der Schleifenkörper hat 10 op

Einsatzbereich:

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 

Der Schleifenkörper hat 3 Op

Die Zeit zum Ausführen von C-Code ist viel kürzer als der Interpretor und kann ignoriert werden.

+32

+1 für die Erklärung einer Antwort mit einer Demontage – TwentyMiles

+2

Eigentlich hat die Schleife Körper in der ersten Demontage 10 Operationen (der Sprung von Position 32 bis 9).In der aktuellen CPython-Implementierung resultiert die Interpretation jedes Bytecodes mit einer ziemlich hohen Wahrscheinlichkeit in einer kostspieligen indirekten Verzweigungsfehlvorhersage in der CPU (der Sprung zur Implementierung des nächsten Bytecodes). Dies ist eine Konsequenz der aktuellen Implementierung von CPython, wobei die JITs, die durch unbeladenes Schlucken, PyPy und andere implementiert werden, höchstwahrscheinlich diesen Overhead verlieren werden. Die besten von ihnen werden auch in der Lage sein, Typspezialisierung für eine Größenordnung Beschleunigung zu tun. –

+2

+1 - @kcwu: Wie hast du es zerlegt? –

3

Da Sie häufiger in Code in C im Interpreter geschrieben ausgeführt werden. i + = 1 ist in Python, also langsam (vergleichsweise), während der Bereich (0, ...) ein C-Aufruf ist, wird die for-Schleife hauptsächlich auch in C ausgeführt.

29

range() ist in C implementiert, während i += 1 interpretiert wird.

Mit xrange() könnte es noch schneller für große Zahlen machen. Beginnend mit Python 3.0 range() ist das gleiche wie zuvor xrange(). siehe

2

Die meisten der in Python integrierten Methodenaufrufe werden als C-Code ausgeführt. Code, der interpretiert werden soll, ist viel langsamer. In Sachen Speichereffizienz und Ausführungsgeschwindigkeit ist der Unterschied gigantisch. Die Python-Interna wurden extrem optimiert, und es ist am besten, diese Optimierungen zu nutzen.

11

Es muss gesagt werden, dass mit der while-Schleife eine Menge an Objekterzeugung und -zerstörung stattfindet.

i += 1 

ist die gleiche wie:

i = i + 1 

Aber weil Python Ints unveränderlich sind, ist es nicht das vorhandene Objekt modifizieren; Vielmehr schafft es ein brandneues Objekt mit einem neuen Wert. Es ist im Grunde:

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

Der Müllsammler wird auch eine große Menge an Aufräumarbeiten zu tun haben. "Objekterstellung ist teuer".

Verwandte Themen