2015-02-13 2 views
10

Ich habe einige Code früher überprüft und der Entwickler schrieb eine Inline if/else anstatt eine get(), um ein Element aus einer Liste abrufen, wenn es existiert (sonst geben Sie es einen Standardwert). Ich entschied mich, etwas timeit Code auf repl zu springen und war ziemlich verwirrt durch das Ergebnis. Die if/else dauert 1/3 der Zeit der get().Warum ist ein Inline-If/Else schneller als ein .get() in Python?

Here is the repl code, und unten ist der Code in der ers sowie das Ergebnis für die Nachwelt:

import timeit 

D = {"a": 1, "b": 2, "c": 3} 

def ef(): return D['a'] if 'a' in D else 1 

def gt(): return D.get('a', 1) 

print "gt1", timeit.timeit(gt, number=10000) 
print "ef1", timeit.timeit(ef, number=10000) 
print "ef2", timeit.timeit(ef, number=10000) 
print "gt2", timeit.timeit(gt, number=10000) 

und die Ergebnisse:

gt1 0.0659999847412 
ef1 0.0239999294281 
ef2 0.0249998569489 
gt2 0.0539999008179 

und eine visuelle von 10 Iterationen der oben timeit Aufrufe, bei denen das Ergebnis für die Darstellung mit 10000 multipliziert wurde

visual of 10 iterations

+2

Warum wäre es nicht schneller? Ich würde fast immer erwarten, dass die integrierte Sprachsyntax schneller ist als ein Methodenaufruf. – Ajedi32

+1

Sie haben die Annahme richtig. Ich habe nur erwartet, dass eine eingebaute Funktion einen niedrigeren Aufwand für ein Wörterbuch hat als eine Bedingung, bei der wir auf den Wert des Wörterbuchs verweisen müssen. – jsanc623

+1

Richtig, aber die Funktion wird natürlich auch den Wert des Wörterbuchs referenzieren müssen, richtig? Wie würde es sonst diesen Wert zurückgeben, wenn der Schlüssel existiert? – Ajedi32

Antwort

28

Der D.get() Pfad enthält einen Attribut-Lookup, und einen Methodenaufruf:

>>> import dis 
>>> D = {"a": 1, "b": 2, "c": 3} 
>>> def gt(): return D.get('a', 1) 
... 
>>> dis.dis(gt) 
    1   0 LOAD_GLOBAL    0 (D) 
       3 LOAD_ATTR    1 (get) 
       6 LOAD_CONST    1 ('a') 
       9 LOAD_CONST    2 (1) 
      12 CALL_FUNCTION   2 
      15 RETURN_VALUE   

Das Attribut Lookup (LOAD_ATTR) vor allem Dinge verlangsamt.

Wenn Sie das Attribut Lookup entfernen (und geben dem in Test eine lokale, mit zu arbeiten), wird das Feld egalisiert:

>>> def gt_fast(D_get=D.get): return D_get('a', 1) 
... 
>>> def ef_fast(D=D): return D['a'] if 'a' in D else 1 
... 
>>> timeit.timeit(gt_fast) 
0.2174091339111328 
>>> timeit.timeit(ef_fast) 
0.2139298915863037 
Verwandte Themen