2009-02-11 4 views
37

Bei der Programmierung in Python ist es möglich, Speicher für eine Liste zu reservieren, die mit einer bekannten Anzahl von Elementen gefüllt wird, so dass die Liste nicht mehrmals während des Erstellens neu zugeordnet wird? Ich habe die Dokumente nach einem Python-Listentyp durchgesehen und nichts gefunden, was dies zu tun scheint. Diese Art der Listenerstellung taucht jedoch in einigen Hotspots meines Codes auf, daher möchte ich sie so effizient wie möglich gestalten.Reservieren Sie Speicher für eine Liste in Python?

Edit: Macht es auch Sinn, so etwas in einer Sprache wie Python zu machen? Ich bin ein ziemlich erfahrener Programmierer, aber neu in Python und immer noch ein Gefühl für seine Art, Dinge zu tun. Ordnet Python intern alle Objekte in separaten Heap-Bereichen zu, wodurch der Zweck des Versuches, Zuordnungen zu minimieren, oder Primitive wie Ints, Floats usw. direkt in Listen gespeichert werden?

+0

Nicht vorzeitig optimieren. – ironfroggy

+20

@ironfroggy: Der Punkt ist, dass dies ** in Hotspots ** aufgetaucht ist. An diesen Orten führte das Erstellen von Listen zu einem signifikanten, realen Flaschenhals **, den Sie optimieren sollten. – dsimcha

+0

mögliches Duplikat von [Python - Erstelle eine Liste mit Anfangskapazität] (http://stackoverflow.com/questions/311775/python-create-a-list-with-initial-capacity) –

Antwort

30

Hier vier Varianten:

  • eine inkrementelle Listenerstellung
  • "vorab zugewiesene" -Liste
  • array.array()
  • numpy.(Nullen)

 

python -mtimeit -s"N=10**6" "a = []; app = a.append;"\ 
    "for i in xrange(N): app(i);" 
10 loops, best of 3: 390 msec per loop 

python -mtimeit -s"N=10**6" "a = [None]*N; app = a.append;"\ 
    "for i in xrange(N): a[i] = i" 
10 loops, best of 3: 245 msec per loop 

python -mtimeit -s"from array import array; N=10**6" "a = array('i', [0]*N)"\ 
    "for i in xrange(N):" " a[i] = i" 
10 loops, best of 3: 541 msec per loop 

python -mtimeit -s"from numpy import zeros; N=10**6" "a = zeros(N,dtype='i')"\ 
    "for i in xrange(N):" " a[i] = i" 
10 loops, best of 3: 353 msec per loop 

Es zeigt, dass [None]*N die schnellste ist und array.array ist die langsamste in diesem Fall.

+0

Ich denke 'array.array' wird hier suboptimal verwendet, siehe meine Antwort. –

+0

@MichailKorobov: guter Fund. 'array ('i', [0]) * n 'entlang ist 10 mal schneller als' array ('i', [0] * n)' obwohl es immer noch langsamer ist als '[0] * n' Variante, wenn Sie Fügen Sie die Initialisierungsschleife hinzu. Der Punkt der Antwort: Messen Sie zuerst. Die Codebeispiele stammen aus anderen Antworten zu der Zeit. – jfs

+3

Dies scheint ein wenig unfair zu numpy und array zu sein, da Sie die Importzeit einschließen, die vermutlich über viele Aufrufe amortisiert werden würde. @ MikhailKorobovs Ergebnisse scheinen darauf hinzudeuten, dass die Anzahl der importierten Zahlen viel schneller ist. –

12

können Sie die Liste der bekannten Länge wie folgt erstellen:

>>> [None] * known_number 
5

In den meisten täglichen Code werden Sie nicht eine solche Optimierung benötigen.

Wenn die Effizienz der Liste jedoch ein Problem wird, sollten Sie als Erstes die generische Liste durch eine von array module getippte ersetzen, die viel effizienter ist.

Hier ist, wie die Liste von 4 Millionen Gleitkommazahlen cound erstellt werden:

import array 
lst = array.array('f', [0.0]*4000*1000) 
+2

Was meinst du mit "viel mehr effizient"? 'array.array' benötigt möglicherweise weniger Speicher, aber eine Python-Liste ist in den meisten Fällen (dh in den Fällen, in denen ich versucht habe) schneller. – jfs

+4

In diesem Fall erstellt es sogar zuerst eine Liste und dann aus der Liste ein Array. Dies ist nicht effizient. –

2

In Python, sind alle Objekte auf dem Heap zugeordnet.
Python verwendet jedoch einen speziellen Speicherzuordner, so dass malloc nicht jedes Mal aufgerufen wird, wenn Sie ein neues Objekt benötigen.
Es gibt auch einige Optimierungen für kleine ganze Zahlen (und dergleichen), die zwischengespeichert werden; Welche Typen und wie, ist jedoch implementierungsabhängig.

4

Wenn Sie Zahlen in Python effizient bearbeiten möchten, dann werfen Sie einen Blick auf NumPy ( http://numpy.scipy.org/). Sie können Dinge extrem schnell erledigen, während Sie immer noch Python verwenden.

tun, was in NumPy Ihre fragen Sie so etwas wie

import numpy as np 
myarray = np.zeros(4000) 

tun würde, die ein Array auf Null initialisiert von Gleitkommazahlen geben würde. Sie können dann sehr coole Dinge tun, wie ganze Arrays durch einen einzelnen Faktor oder durch andere Arrays und andere Sachen (wie in Matlab, wenn Sie das jemals benutzt haben) multiplizieren, was sehr schnell ist (die meiste Arbeit findet in der hochoptimierter C-Teil der NumPy-Bibliothek).

Wenn es sich nicht um Arrays von Zahlen handelt, dann wirst du wahrscheinlich keinen Weg finden, das zu tun, was du in Python willst. Eine Python-Liste von Objekten ist eine Liste von Punkten für Objekte intern (ich denke schon, ich bin kein Experte für Python-Interna), also würde es immer noch seine Mitglieder zuweisen, wenn Sie sie erstellen.

+0

Wie ich auf @Mikhail Korobovs Antwort gesagt habe, ist "np.empty" vorzuziehen, es sei denn, Sie benötigen Ihr Array wirklich, um mit Nullen zu beginnen, was die dreifache Geschwindigkeit auf meinem Computer ergibt. – Mike

8

Werfen Sie einen Blick auf diese:

In [7]: %timeit array.array('f', [0.0]*4000*1000) 
1 loops, best of 3: 306 ms per loop 

In [8]: %timeit array.array('f', [0.0])*4000*1000 
100 loops, best of 3: 5.96 ms per loop 

In [11]: %timeit np.zeros(4000*1000, dtype='f') 
100 loops, best of 3: 6.04 ms per loop 

In [9]: %timeit [0.0]*4000*1000 
10 loops, best of 3: 32.4 ms per loop 

So jemals array.array('f', [0.0]*N), array.array('f', [0.0])*N oder numpy.zeros verwenden nicht verwenden.

+1

Wenn Sie die Array-Elemente setzen, anstatt sie hinzuzufügen, brauchen Sie wahrscheinlich keine Nullen, nur einen reservierten Platz für jedes Element. In diesem Fall ist der Weg "np.empty" anstelle von "np.zeros". Mit deinem Test ist das dreimal schneller auf meinem Computer. – Mike

Verwandte Themen