Seite 1 von 1
list comprehension
Verfasst: Mittwoch 7. März 2012, 00:13
von myxin
Guten Abend,
im Web hatte ich den Eindruck gewonnen, das Pythons List Comprehension aus Performancesicht besser ist als andere Alternative (Aussnahme threads etc ...)
Bei diesem simplen Beispiel ist dies aber nicht der Fall.
Code: Alles auswählen
import timeit
def p1(s):
j = [i*2 for i in s]
def p2(s):
for i in s:
j = i*2
t1 = timeit.Timer("p1(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])", "from __main__ import p1")
print "p1: ", t1.timeit()
t2 = timeit.Timer("p2(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])", "from __main__ import p2")
print "p2: ", t2.timeit()
print "p1: ", min(t1.repeat(100, 10000))
print "p2: ", min(t2.repeat(100, 10000))
p1: 4.45716592472
p2: 3.11650950051
p1: 0.0429442848183
p2: 0.0300219720663
Frage: Worin besteht normalerweise der Performancegewinn bei List Comprehension oder was muss da gegeben sein, damit dies schneller ist?
Danke
Claudia
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 00:28
von DasIch
In p1 wird eine Liste erzeugt in p2 nicht, theoretisch könnte der Interpreter p2 soweit optimieren dass sie konstante Laufzeit hat. Dementsprechend sind die Ergebnisse des Vergleichs uninteressant weil sie nicht mehr Aussagen als dass unterschiedliche Algorithmen unterschiedliche Laufzeiten haben.
Wenn p2 dass gleiche wie p1 tun würde nur eben ohne LC, wäre p1 wahrscheinlich langsamer weil die zusätzlichen Methodenaufrufe recht viel Zeit kosten.
Ein weitere Vorteil von LCs ist, dass der Interpreter Allokationen weg optimieren kann wenn es möglich ist die Länge der Liste im voraus zu bestimmten.
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 00:42
von myxin
@DasIch
da hast du natürlich Recht - fehlerhaften Code vergleichen macht wenig Sinn.
Wenn p2 dann geändert wurde, ist p1 schneller.
ok - heisst also dann, LC ist schneller wenn es darum geht eine Liste zu bearbeiten und in eine andere Liste zu übergeben, korrekt?
Danke
Claudia
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 01:01
von deets
DasIch hat geschrieben:
Ein weitere Vorteil von LCs ist, dass der Interpreter Allokationen weg optimieren kann wenn es möglich ist die Länge der Liste im voraus zu bestimmten.
Ist das eine faktische Optimierung in CPython?
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 01:31
von DasIch
myxin hat geschrieben:ok - heisst also dann, LC ist schneller wenn es darum geht eine Liste zu bearbeiten und in eine andere Liste zu übergeben, korrekt?
LCs sind eigentlich immer schneller wenn du eine Liste erzeugst.
deets hat geschrieben:Ist das eine faktische Optimierung in CPython?
(Noch) nicht, es gibt ein
Issue sowie einen Patch der die Optimierung implementiert. PyPy hat die Optimierung in einem Branch.
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 10:34
von BlackJack
@myxin: Wenn Du LCs einsetzt weil sie schneller sind, dann ist Python vielleicht nicht die richtige Sprache für Dich. LCs verwendet man wenn man eine Liste aus einem iterierbaren Objekt erstellen möchte in dem man mit jedem Element etwas anstellt und eventuell welche aussortieren möchte, und die kompakte Schreibweise an der Stelle lesbarer ist. Nicht um etwas schneller zu machen. Das ist ein netter Nebeneffekt, sollte aber in der Regel nicht die Hauptmotivation dafür sein LCs einzusetzen.
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 19:23
von myxin
@BlackJack, nein, Performance war nicht hautpsächlich der Grund der Frage.
Primär wollte ich verstehen, warum dies so ist,
im 2ten Schritt will ich aber auch effizienten Code schreiben können.
Das heisst natürlich auch, dass dies letztendlich immer von Fall zu Fall entschieden werden muss
und es daher wichtig ist, zu verstehen warum etwas so ist.
Vielen Dank für die Hilfen
Claudia
Re: list comprehension
Verfasst: Mittwoch 7. März 2012, 20:52
von BlackJack
Hauptsächlich ist das schneller weil bei einer LC implizit ist, dass an eine Liste angehängt wird, während die Alternative ohne LC bei jedem Schleifendurchlauf das ``append``-Attribut von dem Ergebnis-Objekt abfragen muss. Das sieht man zum Beispiel wenn man sich von CPython den Bytecode disassemblieren lässt:
Code: Alles auswählen
In [195]: def f1(it):
.....: r = []
.....: for x in it:
.....: r.append(x * 2)
.....: return r
.....:
In [196]: def f2(it):
.....: return [x * 2 for x in it]
.....:
In [197]: import dis
In [198]: dis.dis(f1)
2 0 BUILD_LIST 0
3 STORE_FAST 1 (r)
3 6 SETUP_LOOP 31 (to 40)
9 LOAD_FAST 0 (it)
12 GET_ITER
>> 13 FOR_ITER 23 (to 39)
16 STORE_FAST 2 (x)
4 19 LOAD_FAST 1 (r)
22 LOAD_ATTR 0 (append)
25 LOAD_FAST 2 (x)
28 LOAD_CONST 1 (2)
31 BINARY_MULTIPLY
32 CALL_FUNCTION 1
35 POP_TOP
36 JUMP_ABSOLUTE 13
>> 39 POP_BLOCK
5 >> 40 LOAD_FAST 1 (r)
43 RETURN_VALUE
In [199]: dis.dis(f2)
2 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 1 (_[1])
7 LOAD_FAST 0 (it)
10 GET_ITER
>> 11 FOR_ITER 17 (to 31)
14 STORE_FAST 2 (x)
17 LOAD_FAST 1 (_[1])
20 LOAD_FAST 2 (x)
23 LOAD_CONST 1 (2)
26 BINARY_MULTIPLY
27 LIST_APPEND
28 JUMP_ABSOLUTE 11
>> 31 DELETE_FAST 1 (_[1])
34 RETURN_VALUE
Re: list comprehension
Verfasst: Freitag 9. März 2012, 19:20
von myxin
@BlackJack
vielen Dank für den Tipp mit import dis, kannte ich auch noch nicht.
Leider verstehe ich nicht, was der Auszug bedeutet.
Muss mich dazu mal einlesen und ggf. nachfragen
Gruß
Claudia
Re: list comprehension
Verfasst: Freitag 9. März 2012, 20:19
von EyDu
Das dis-Modul musst du nicht unbedingt versehen. BlackJack hat lediglich demonstriert, in welchen Bytecode welche Funktionen umgewandelt werden. Praktisch ist das für dich sicher nicht unbedingt relevant.