Warum ist Tupel schneller als die Liste?

Ich habe gerade in "Dive into Python" gelesen, dass "Tupel sind schneller als Listen".

Tupel ist unveränderlich, und die Liste ist veränderlich, aber ich verstehe nicht ganz, warum Tupel schneller ist.

Wer hat hier einen Performance-Test gemacht?

5 Solutions collect form web for “Warum ist Tupel schneller als die Liste?”

Das berichtete "Geschwindigkeit des Bauverhältnisses" gilt nur für konstante Tupel (diejenigen, deren Gegenstände durch Literale ausgedrückt werden). Beachten Sie sorgfältig (und wiederholen Sie auf Ihrem Computer – Sie müssen nur die Befehle in einem Shell / Befehlsfenster eingeben!) …:

$ python3.1 -mtimeit -s'x,y,z=1,2,3' '[x,y,z]' 1000000 loops, best of 3: 0.379 usec per loop $ python3.1 -mtimeit '[1,2,3]' 1000000 loops, best of 3: 0.413 usec per loop $ python3.1 -mtimeit -s'x,y,z=1,2,3' '(x,y,z)' 10000000 loops, best of 3: 0.174 usec per loop $ python3.1 -mtimeit '(1,2,3)' 10000000 loops, best of 3: 0.0602 usec per loop $ python2.6 -mtimeit -s'x,y,z=1,2,3' '[x,y,z]' 1000000 loops, best of 3: 0.352 usec per loop $ python2.6 -mtimeit '[1,2,3]' 1000000 loops, best of 3: 0.358 usec per loop $ python2.6 -mtimeit -s'x,y,z=1,2,3' '(x,y,z)' 10000000 loops, best of 3: 0.157 usec per loop $ python2.6 -mtimeit '(1,2,3)' 10000000 loops, best of 3: 0.0527 usec per loop 

Ich habe nicht die Messungen auf 3.0 gemacht, natürlich habe ich es nicht herum – es ist total veraltet und es gibt absolut keinen Grund, es zu behalten, denn 3.1 ist es in jeder Hinsicht überlegen (Python 2.7, wenn du Kann auf sie aufsteigen, misst als fast 20% schneller als 2.6 in jeder Aufgabe – und 2.6, wie du siehst, ist schneller als 3.1 – also, wenn du dich ernsthaft um die Leistung kümmertest, ist Python 2.7 wirklich das einzige Release, das du haben solltest Gehen für!).

Jedenfalls ist hier der entscheidende Punkt, dass in jedem Python-Release der Aufbau einer Liste aus konstanten Literalen etwa die gleiche Geschwindigkeit oder etwas langsamer ist, als sie aus Werten, die von Variablen referenziert werden, zu bauen; Aber Tupel verhalten sich sehr unterschiedlich – das Aufbauen eines Tupels aus konstanten Literalen ist typischerweise dreimal so schnell wie es aus Werten hervorgeht, die von Variablen referenziert werden! Sie können sich fragen, wie das sein kann, richtig? -)

Antwort: Ein Tupel aus konstanten Literalen kann leicht durch den Python-Compiler als ein, unveränderliches, konstantes Literal selbst identifiziert werden: so ist es im Wesentlichen nur einmal gebaut, wenn der Compiler die Quelle in Bytecodes verwandelt und in der "Konstanten-Tabelle verstaut wird "Der relevanten Funktion oder Modul. Wenn diese Bytecodes ausführen, müssen sie nur das vorgefertigte konstante Tupel wiederherstellen – hey presto! -)

Diese einfache Optimierung kann nicht auf Listen angewendet werden, da eine Liste ein veränderliches Objekt ist, also ist es entscheidend, dass, wenn der gleiche Ausdruck wie [1, 2, 3] zweimal ausgeführt wird (in einer Schleife – das timeit Modul macht die Schleife an In deinem Namen ;-), ein neues neues Listenobjekt wird jedesmal neu konstruiert – und diese Konstruktion (wie die Konstruktion eines Tupels, wenn der Compiler es nicht trivial als Kompilierzeitkonstante und unveränderliches Objekt identifizieren kann) dauert eine Weile .

Das heißt, die Tupel-Konstruktion (wenn beide Konstruktionen tatsächlich auftreten müssen) ist immer noch doppelt so schnell wie die List-Konstruktion – und diese Diskrepanz kann durch die schiere Einfachheit des Tupels erklärt werden, die andere Antworten wiederholt erwähnt haben. Aber diese Einfachheit ist nicht für eine Beschleunigung von sechs Mal oder mehr, wie Sie beobachten, wenn Sie nur vergleichen die Konstruktion von Listen und Tupel mit einfachen konstanten Literalen als ihre Elemente! _)

Mit der Kraft des timeit Moduls kannst du oft auch leistungsbezogene Fragen lösen:

 $ python2.6 -mtimeit -s 'a = tuple(range(10000))' 'for i in a: pass' 10000 loops, best of 3: 189 usec per loop $ python2.6 -mtimeit -s 'a = list(range(10000))' 'for i in a: pass' 10000 loops, best of 3: 191 usec per loop 

Dies zeigt, dass Tupel ist vernachlässigbar schneller als Liste für Iteration. Ich bekomme ähnliche Ergebnisse für die Indizierung, aber für den Bau, Tupel zerstört Liste:

 $ python2.6 -mtimeit '(1, 2, 3, 4)' 10000000 loops, best of 3: 0.0266 usec per loop $ python2.6 -mtimeit '[1, 2, 3, 4]' 10000000 loops, best of 3: 0.163 usec per loop 

Also, wenn Geschwindigkeit der Iteration oder Indexierung sind die einzigen Faktoren, gibt es effektiv keinen Unterschied, aber für den Bau, Tupel gewinnen.

Alex hat eine gute Antwort gegeben, aber ich werde versuchen, auf ein paar Dinge zu erweitern, die ich erwähne erwähne. Alle Leistungsunterschiede sind in der Regel klein und implementierungsspezifisch: also wette nicht die Farm auf ihnen.

In CPython werden Tupel in einem einzigen Speicherblock gespeichert, so dass das Erstellen eines neuen Tupels im schlimmsten Fall einen einzigen Aufruf zur Speicherung des Speichers beinhaltet. Listen werden in zwei Blöcken zugewiesen: die feste mit allen Python-Objektinformationen und ein variabler Block für die Daten. Das ist ein Teil des Grundes, warum die Schaffung eines Tupels ist schneller, aber es wahrscheinlich auch erklärt, die leichte Unterschied in der Indexierung Geschwindigkeit, da gibt es einen weniger Zeiger zu folgen.

Es gibt auch Optimierungen in CPython, um Speicherzuweisungen zu reduzieren: de-zugeordnete Listenobjekte werden auf einer freien Liste gespeichert, so dass sie wiederverwendet werden können, aber die Zuweisung einer nicht leeren Liste erfordert noch eine Speicherzuordnung für die Daten. Tupel werden auf 20 kostenlosen Listen für Tupel unterschiedlicher Größe gespeichert, so dass die Zuteilung eines kleinen Tupels oft keine Speicherzuweisungsaufrufe erfordert.

Optimierungen wie diese sind in der Praxis hilfreich, aber sie können es auch riskant machen, sich zu sehr auf die Ergebnisse von 'timeit' zu verlassen und natürlich ganz anders, wenn man sich auf etwas wie IronPython bewegt, wo die Speicherzuteilung ganz anders funktioniert.

Im Wesentlichen, weil Tuples Unveränderlichkeit bedeutet, dass der Dolmetscher eine schlankere, schnellere Datenstruktur für sie verwenden kann, verglichen mit der Liste.

Ein Bereich, in dem eine Liste vor allem schneller ist, ist die Konstruktion von einem Generator, und insbesondere sind die Listenverfassungen viel schneller als das nächstliegende Tupeläquivalent, tuple() mit einem Generatorargument:

 $ python --version Python 3.6.0rc2 $ python -m timeit 'tuple(x * 2 for x in range(10))' 1000000 loops, best of 3: 1.34 usec per loop $ python -m timeit 'list(x * 2 for x in range(10))' 1000000 loops, best of 3: 1.41 usec per loop $ python -m timeit '[x * 2 for x in range(10)]' 1000000 loops, best of 3: 0.864 usec per loop 

Beachten Sie insbesondere, dass tuple(generator) ein bisschen schneller als die list(generator) zu sein scheint, aber [elem for elem in generator] ist viel schneller als beide.

  • Unveränderliche Liste in Python
  • Wie man einen Text in Tupel in einer Liste in Python umwandelt
  • Ziemlich eine Liste in einem tabellarischen Format
  • Pass mehrere Argumente in Form von Tupel
  • So entnehmen Sie Tupel-Daten in Single-Element-Format
  • Zählen von Elementen innerhalb von Tupeln in Python
  • Wählen Sie den Wert aus der Liste der Tupel aus
  • Verknüpfen Sie Elemente eines Tupels in einer Liste in Python
  • Python-Liste von (str, int) Tupel Wörterbücher
  • Multiplizieren Sie benachbarte Elemente
  • Warum sind Python-Strings und Tupel unveränderlich gemacht?
  • Python ist die beste Programmiersprache der Welt.