2009-02-11 11 views
37

Lors de la programmation en Python, est-il possible de réserver de la mémoire pour une liste qui sera peuplée avec un nombre connu d'éléments, de sorte que la liste ne sera pas réallouée plusieurs fois en la construisant? J'ai regardé les docs pour un type de liste Python, et n'ai rien trouvé qui semble le faire. Cependant, ce type de construction de liste apparaît dans quelques zones sensibles de mon code, donc je veux le rendre aussi efficace que possible.Réserver de la mémoire pour la liste en Python?

Editer: Aussi, est-il même logique de faire quelque chose comme ça dans un langage comme Python? Je suis un programmeur assez expérimenté, mais nouveau à Python et toujours avoir une idée de sa façon de faire les choses. Python alloue-t-il en interne tous les objets dans des espaces de segment séparés, en essayant de minimiser les allocations, ou les primitives telles que ints, floats, etc. sont-elles stockées directement dans les listes?

+0

Ne pas optimiser prématurément. – ironfroggy

+20

@ironfroggy: Le fait est que cela ** est apparu dans les hotspots **. Dans ces endroits, la construction de listes causait ** un goulot d'étranglement important et réel **, le type que vous devriez optimiser. – dsimcha

+0

duplication possible de [Python - Créer une liste avec la capacité initiale] (http://stackoverflow.com/questions/311775/python-create-a-list-with-initial-capacity) –

Répondre

30

est ici quatre variantes:

  • une création incrémentale liste
  • "pré-alloué" liste
  • array.array()
  • numpy.zéros()

 

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 

Il montre que [None]*N est le plus rapide et array.array est le plus lent dans ce cas.

+0

Je pense que 'array.array' est utilisé de façon suboptimale ici, voir ma réponse. –

+0

@MikhailKorobov: bonne trouvaille. 'array ('i', [0]) * n' long est 10 fois plus rapide que' array ('i', [0] * n) 'bien qu'il soit encore plus lent que la variante [0] * n' si vous ajoutez la boucle d'initialisation. Le point de la réponse: mesurer d'abord. Les exemples de code proviennent d'autres réponses à la fois. – jfs

+3

Cela semble un peu injuste pour numpy et tableau puisque vous incluez le temps d'importation, qui serait probablement amorti sur un grand nombre d'appels. @ Les résultats de MikhailKorobov semblent suggérer que numpy, une fois importé, est beaucoup plus rapide. –

12

vous pouvez créer une liste de la longueur connue comme ceci:

>>> [None] * known_number 
5

Dans la plupart des codes de tous les jours vous ne serez pas besoin d'une telle optimisation.

Cependant, lorsque l'efficacité de la liste devient un problème, la première chose à faire est de remplacer la liste générique par celle de array module qui est beaucoup plus efficace.

Voici comment la liste est de 4 millions de nombres à virgule flottante cound créé:

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

Que voulez-vous dire par "beaucoup plus" efficace"? 'array.array' peut nécessiter moins de mémoire, mais une liste Python est plus rapide dans la plupart des cas (c'est-à-dire ceux que j'ai essayés). – jfs

+4

Dans ce cas, il crée même d'abord une liste, puis de la liste un tableau. Ce n'est pas efficace. –

2

En Python, tous les objets sont alloués sur le tas.
Mais Python utilise un allocateur de mémoire spécial, donc malloc ne sera pas appelé chaque fois que vous aurez besoin d'un nouvel objet.
Il existe également des optimisations pour les petits entiers (et autres) qui sont mis en cache; Cependant, quels types, et comment, dépend de l'implémentation.

4

Si vous souhaitez manipuler les nombres efficacement en Python, jetez un coup d'œil à NumPy ( http://numpy.scipy.org/). Il vous permet de faire des choses extrêmement rapidement tout en utilisant Python.

Pour faire ce que votre demande en NumPy vous feriez quelque chose comme

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

qui vous donnerait un tableau de nombres à virgule flottante initialisés à zéro. Vous pouvez alors faire des choses très cool comme multiplier des tableaux entiers par un seul facteur ou par d'autres tableaux et d'autres choses (comme dans Matlab si vous en avez déjà utilisé), ce qui est très rapide (la plupart du travail se passe dans le partie C hautement optimisée de la bibliothèque NumPy).

Si ce n'est pas des tableaux de nombres, alors vous ne trouverez probablement pas un moyen de faire ce que vous voulez en Python. Une liste d'objets Python est une liste de points pour les objets en interne (je pense que de toute façon, je ne suis pas un expert en interne de Python) donc il allouerait toujours chacun de ses membres quand vous les créeriez.

+0

Comme je l'ai dit sur la réponse @Mikhail Korobov, 'np.empty' est préférable, sauf si vous avez vraiment besoin de votre tableau pour commencer avec des zéros, ce qui donne le triple de la vitesse sur mon ordinateur. – Mike

8

Jetez un oeil à ceci:

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 

donc ne jamais utiliser array.array('f', [0.0]*N), utilisez array.array('f', [0.0])*N ou numpy.zeros.

+1

Si vous définissez les éléments du tableau au lieu de les ajouter, vous n'avez probablement pas besoin de zéros, juste un espace réservé pour chaque élément. Dans ce cas, le chemin à parcourir est 'np.empty' à la place de' np.zeros'. Avec votre test, c'est trois fois plus rapide sur mon ordinateur. – Mike

Questions connexes