2009-02-11 25 views
37

Cuando se programa en Python, ¿es posible reservar memoria para una lista que se completará con un número conocido de elementos, para que la lista no se reasigne varias veces al construirla? He buscado en los documentos un tipo de lista de Python, y no he encontrado nada que parezca hacer esto. Sin embargo, este tipo de creación de listas aparece en algunas zonas activas de mi código, por lo que quiero que sea lo más eficiente posible.¿Memoria de reserva para la lista en Python?

Editar: Además, ¿tiene sentido hacer algo como esto en un lenguaje como Python? Soy un programador bastante experimentado, pero nuevo en Python y todavía estoy sintiendo su forma de hacer las cosas. ¿Python internamente asigna todos los objetos en espacios de montón separados, lo que infringe el propósito de tratar de minimizar las asignaciones, o son primitivas como ints, flotantes, etc. almacenados directamente en listas?

+0

No optimice prematuramente. – ironfroggy

+20

@ironfroggy: El punto es que esto ** apareció en puntos de acceso **. En estos lugares, la construcción de listas estaba causando un ** cuello de botella significativo en el mundo real **, del tipo que debería optimizar. – dsimcha

+0

posible duplicado de [Python: cree una lista con capacidad inicial] (http://stackoverflow.com/questions/311775/python-create-a-list-with-initial-capacity) –

Respuesta

30

Aquí hay cuatro variantes:

  • una lista incrementales creación
  • lista de "pre-asignado"
  • array.array()
  • numpy.ceros()

 

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 

Muestra que [None]*N es el más rápido y array.array es el más lento en este caso.

+0

Creo que 'array.array' se usa aquí de manera subóptima, mira mi respuesta. –

+0

@MikhailKorobov: buen descubrimiento. 'array ('i', [0]) * n' along es 10 veces más rápido que' array ('i', [0] * n) 'aunque es aún más lento que la variante' [0] * n' si agregue el ciclo de inicialización. El punto de la respuesta: mide primero. Los ejemplos de código son de otras respuestas en el momento. – jfs

+3

Esto parece un poco injusto para numpy y array, ya que está incluido el tiempo de importación, que probablemente se amortizará en un gran número de llamadas. Los resultados de @ MikhailKorobov parecen sugerir que Numpy, una vez importado, es mucho más rápido. –

12

puede crear lista de la longitud conocida como esto:

>>> [None] * known_number 
5

En la mayor parte de código de todos los días no necesitará dicha optimización.

Sin embargo, cuando la eficiencia de la lista se convierte en un problema, lo primero que debe hacer es reemplazar la lista genérica con una escrita desde array module que es mucho más eficiente.

Así es como se crea la lista de 4 millones de coma flotante de los números cound:

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

¿Qué quiere decir con "mucho más"? eficiente"? 'array.array' podría requerir menos memoria, pero una lista de Python es más rápida en la mayoría de los casos (es decir, los que he probado). – jfs

+4

En este caso, incluso crea primero una lista y luego de la lista una matriz. Esto no es eficiente. –

2

En Python, todos los objetos se asignan en el montón.
Pero Python usa un asignador de memoria especial para que no se invoque malloc cada vez que necesite un objeto nuevo.
También hay algunas optimizaciones para enteros pequeños (y similares) que se almacenan en caché; sin embargo, qué tipos y cómo depende la implementación.

4

Si quiere manipular números de manera eficiente en Python, eche un vistazo a NumPy ( http://numpy.scipy.org/). Te permite hacer cosas extremadamente rápido mientras usas Python.

para hacer lo que pedía en su NumPy que haría algo así como

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

lo que le daría una serie de números de punto flotante inicializados a cero. A continuación, puedes hacer cosas geniales como matrices multiplas completas por un solo factor o por otras matrices y otras cosas (algo así como en Matlab si alguna vez has usado eso) que es muy rápido (la mayor parte del trabajo real está sucediendo en el parte C altamente optimizada de la biblioteca NumPy).

Si no se trata de matrices de números, es probable que después no encuentre la manera de hacer lo que quiera en Python. Una lista de objetos de Python es una lista de puntos a objetos internamente (creo que sí, no soy un experto de Python en el interior) por lo que seguiría asignando cada uno de sus miembros a medida que los creas.

+0

Como dije en la respuesta de @Mikhail Korobov, 'np.empty' es preferible a menos que realmente necesite que su matriz empiece con ceros, dando el triple de velocidad a mi computadora. – Mike

8

Tome un vistazo a esto:

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 

Así que no lo utilices array.array('f', [0.0]*N), utilice array.array('f', [0.0])*N o numpy.zeros.

+1

Si va a configurar los elementos de la matriz en lugar de agregarlos, probablemente no necesite ceros, solo un espacio reservado para cada elemento. En este caso, el camino a seguir es 'np.empty' en lugar de' np.zeros'. Con su prueba, eso es tres veces más rápido en mi computadora. – Mike

Cuestiones relacionadas