Warum ist startswith langsamer als slicing
Warum ist die Umsetzung von startwith
langsamer als schneiden?
In [1]: x = 'foobar'
In [2]: y = 'foo'
In [3]: %timeit x.startswith(y)
1000000 loops, best of 3: 321 ns per loop
In [4]: %timeit x[:3] == y
10000000 loops, best of 3: 164 ns per loop
Überraschend, sogar einschließlich der Berechnung für die Länge, schneiden, scheint immer noch deutlich schneller:
In [5]: %timeit x[:len(y)] == y
1000000 loops, best of 3: 251 ns per loop
Hinweis: der erste Teil dieses Verhaltens wird darauf hingewiesen, in Python for Data Analysis (Kapitel 3), aber keine Erklärung dafür angeboten wird.
.
Falls hilfreich: hier ist der C-code für startswith
; und hier ist die Ausgabe von dis.dis
:
In [6]: import dis
In [7]: dis_it = lambda x: dis.dis(compile(x, '<none>', 'eval'))
In [8]: dis_it('x[:3]==y')
1 0 LOAD_NAME 0 (x)
3 LOAD_CONST 0 (3)
6 SLICE+2
7 LOAD_NAME 1 (y)
10 COMPARE_OP 2 (==)
13 RETURN_VALUE
In [9]: dis_it('x.startswith(y)')
1 0 LOAD_NAME 0 (x)
3 LOAD_ATTR 1 (startswith)
6 LOAD_NAME 2 (y)
9 CALL_FUNCTION 1
12 RETURN_VALUE
- Schneiden ist wahrscheinlich optimiert, stärker auf C-Ebene
- dies entspricht nicht dem code. können Sie ein Tupel zu
startswith
können Siestart
undend
Parameter, die Pflege und das kostet Zeit. - Wenn Sie sprechen über die Optimierung der zugrunde liegenden Betriebssystems und die Architektur der CPU sind sehr wichtig. Ist das eine x86 - (Intel oder AMD) oder x86-64bit? oder ein ARM-core, windows oder linux...?
- Dies ist ein Intel x86-64-unter osx. Ich hatte gedacht, dass (weitgehend) C (und damit CPython) - Implementierungen auf allen Plattformen vergleichbar wäre (D. H. wenn man Architektur läuft der code langsamer, dann werden es auch andere)...?
- Unterschiede version von OS-standard-Bibliotheken, die version des Compilers verwendet, die Menge des verfügbaren Speichers wenn Sie den test ausführen, die hintergrund-tasks/Prozesse laufen, ob der Prozessor von Intel oder AMD, etc etc können ALLE verursachen Variationen in der benchmark-Ergebnisse. Vor allem, wenn Sie ' re Umgang mit einem wiederholten test (wobei die Werte sind die gleichen-und Nanosekunden-Messungen werden verglichen. Sie könnte knapp werden in Zwischenspeichern und branch prediction Optimierungen auf der Architektur-Ebene könnte verzerren die Ergebnisse.
- Ich bin damit einverstanden, die zahlen selbst werden verschiedene (wenn ich Sie laufen zweimal, Sie sind anders!), aber ich wäre überrascht, wenn erhebliche Unterschiede (D. H. eine schneller als der andere) wurden nicht reflektiert, die auf allen Plattformen aufgrund der Laufzeit der C-compiler. Ich hatte nicht darüber nachgedacht "Cache-und branch predictions" auf einer Architektur-Ebene... interessant.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Einige der performance-Unterschied kann erklärt werden unter Berücksichtigung der Zeit, die die
.
Betreiber zu tun, seine Sache:Einem anderen Teil des Unterschiedes kann durch die Tatsache erklärt werden, dass
startswith
ist ein Funktion, und auch keine op-Funktion ruft ein wenig Zeit nehmen:Diese nicht völlig erklären Sie den Unterschied, da die version mit den schneiden und das
len
ruft eine Funktion und ist immer noch schneller (Vergleich zusw(y)
oben -- 267 ns):Meine einzige Vermutung ist, dass hier vielleicht Python optimiert lookup-Zeit built-in-Funktionen, oder dass
len
Anrufe sind stark optimiert ist (was wohl stimmt). Könnte es möglich sein, zu testen, die mit einer benutzerdefiniertenlen
func. Oder vielleicht ist das, wo die Unterschiede identifiziert LastCoder kick in. Beachten Sie auch larsmans' Ergebnisse, die darauf hindeuten, dassstartswith
ist tatsächlich schneller für mehr Saiten. Die ganze Argumentation oben gilt nur für diejenigen Fälle, in denen der overhead-ich Rede tatsächlich von Bedeutung.Der Vergleich ist nicht fair, da Sie nur Messen der Fall, wo
startswith
zurückTrue
.Auch, für viel mehr Streicher
startswith
ist viel schneller:Dies ist immer noch wahr, wenn es keine übereinstimmung.
So,
startswith
ist wahrscheinlich langsamer für kurze strings, weil es optimiert für lange.(Trick zufällige Zeichenfolgen entnommen diese Antwort.)
startswith
ist komplexer als slicing...Dies ist nicht ein einfaches Zeichen vergleichen-Schleife für die Nadel in den Anfang von haystack, dass das passiert. Wir suchen eine for-Schleife, die die Iteration über einen Vektor/Tupel (subobj) und das aufrufen einer weiteren Funktion (
_string_tailmatch
) auf. Mehrere Funktionsaufrufe haben overhead mit Bezug auf den stack, argument-sanity-checks etc...startswith
ist eine library-Funktion, während das aufschneiden zu sein scheint, eingebaut in die Sprache.Zitieren die docs,
startswith
nicht mehr, denken Sie vielleicht:Aufruf einer Funktion ist Recht teuer. Ich weiß jedoch nicht, ob dies der Fall ist, als auch für die built-in-Funktionen in C geschrieben.
Seien Sie sich bewusst, obwohl, dass slicing kann es sich um einen Funktionsaufruf als auch abhängig von dem Objekt, das verwendet wird.