2016-05-05 8 views
11

Diese question und meine answer haben mich über diesen eigenartigen Unterschied zwischen Python 2.7 und Python 3.4 nachdenken lassen. Nehmen Sie den einfachen Beispielcode:Ist das Schneiden in Python 3.4 wirklich langsamer?

import timeit 
import dis 

c = 1000000 
r = range(c) 
def slow(): 
    for pos in range(c): 
     r[pos:pos+3] 

dis.dis(slow) 

time = timeit.Timer(lambda: slow()).timeit(number=1) 
print('%3.3f' % time) 

In Python 2.7, ich 0.165~ konsequent erhalten und für Python 3.4 ich konsequent 0.554~ bekommen. Der einzige wesentliche Unterschied zwischen den Disassemblies besteht darin, dass Python 2.7 den Bytecode SLICE+3 ausgibt, während Python 3.4 BUILD_SLICE gefolgt von BINARY_SUBSCR ausgibt. Beachten Sie, dass ich die Kandidaten für eine mögliche Verlangsamung von der anderen Frage eliminiert habe, nämlich Strings und die Tatsache, dass xrange nicht in Python 3.4 existiert (die der Klasse range von letzterem ähnlich sein soll).

Mit itertools'islice ergibt fast identische Timings zwischen den beiden, so dass ich sehr vermuten, dass es das Schneiden ist, das ist die Ursache für den Unterschied hier.

Warum passiert das und gibt es einen Link zu einer autoritativen Quelle, die Verhaltensänderungen dokumentiert?

EDIT: Als Antwort auf die Antwort habe ich die range Objekte in list gewickelt, die eine spürbare Beschleunigung gegeben haben. Als ich jedoch die Anzahl der Iterationen in timeit erhöhte, bemerkte ich, dass die zeitlichen Unterschiede immer größer wurden. Als Vernunftprüfung habe ich den Slicing durch None ersetzt, um zu sehen, was passieren würde.

500 Iterationen in timeit.

c = 1000000 
r = list(range(c)) 
def slow(): 
    for pos in r: 
     None 

ergibt 10.688 und 9.915 sind. Das Ersetzen der for-Schleife durch for pos in islice(r, 0, c, 3) ergibt 7.626 bzw. 6.270. Ersetzen von None durch r[pos] ergab 20~ bzw. 28~. r[pos:pos+3] ergibt 67.531 bzw. 106.784.

Wie Sie sehen können, sind die Timing-Unterschiede sehr groß. Ich bin immer noch überzeugt, dass das Problem nicht direkt mit range zusammenhängt.

+0

Haben Sie 'range' oder' xrange' in Python 2 verwendet? – cdarke

+0

Versuchen Sie es erneut mit 'r = list (range (c))'. –

+0

Python 3 'range' und Python 2' xrange' Objekte sind in diesem Kontext nicht ähnlich. Python 3 'range' Objekte unterstützen Slicing, Python 2' xrange' Objekte unterstützen kein Slicen. – cdarke

Antwort

10

In Python 2.7 iterieren Sie über eine Liste und schneiden eine Liste auf. In Python 3.4 iterieren Sie über range und einen range.

Wenn ich einen Test mit einer Liste auf beiden Python-Versionen laufen:

from __future__ import print_function 
import timeit 
print(timeit.timeit('x[5:8]', setup='x = list(range(10))')) 

ich 0.243554830551 seconds on Python 2.7 und 0.29082867689430714 seconds on Python 3.4, ein viel kleinerer Unterschied.


Der Performance-Unterschied Sie nach Beseitigung der range Objekt sehen, ist viel kleiner. Es kommt hauptsächlich von zwei Faktoren: Addition ist ein bisschen langsamer auf Python 3, und Python 3 muss durch __getitem__ mit einem Slice-Objekt für Slicing gehen, während Python 2 __getslice__ hat.

Ich konnte die Zeitdifferenz, die Sie mit r[pos] gesehen haben, nicht replizieren. Sie könnten einen gewissen Störfaktor in diesem Test gehabt haben.

+1

Die Erhöhung der Anzahl der Iterationen zeigt immer größere Lücken in den Timing-Unterschieden. '1.352' und' 2.171' jeweils für 10 Iterationen. Ich bin mir nicht sicher, was ich hier vermisse. –

+0

@ user6292850: Wie läuft der Test? – user2357112

+1

Ich erweitere lediglich den Parameter number in 'timeit' und führe' python test.py' aus, gefolgt von 'python3 test.py'. –