La indexación y / o la adición de matrices numpy parece lenta

Estaba jugando con matrices numpy de puntos de referencia porque estaba obteniendo resultados más lentos de lo esperado cuando intentaba reemplazar matrices python con matrices numpy en una secuencia de comandos.

Sé que me estoy perdiendo algo, y esperaba que alguien pudiera aclarar mi ignorancia.

Creé dos funciones y las cronometré.

NUM_ITERATIONS = 1000 def np_array_addition(): np_array = np.array([1, 2]) for x in xrange(NUM_ITERATIONS): np_array[0] += x np_array[1] += x def py_array_addition(): py_array = [1, 2] for x in xrange(NUM_ITERATIONS): py_array[0] += x py_array[1] += x 

Resultados:

 np_array_addition: 2.556 seconds py_array_addition: 0.204 seconds 

¿Lo que da? ¿Qué está causando la desaceleración masiva? Pensé que si estuviera usando matrices de tamaño estático, el número sería al menos la misma velocidad.

¡Gracias!

Actualizar:

Me molestaba que el acceso a la matriz numpy fuera lento, y pensé “Oye, solo son matrices en la memoria, ¿verdad? ¡Cython debería resolver esto!”

Y lo hizo. Aquí está mi punto de referencia revisado

 import numpy as np cimport numpy as np ctypedef np.int_t DTYPE_t NUM_ITERATIONS = 200000 def np_array_assignment(): cdef np.ndarray[DTYPE_t, ndim=1] np_array = np.array([1, 2]) for x in xrange(NUM_ITERATIONS): np_array[0] += 1 np_array[1] += 1 def py_array_assignment(): py_array = [1, 2] for x in xrange(NUM_ITERATIONS): py_array[0] += 1 py_array[1] += 1 

np_array a cdef np.ndarray[DTYPE_t, ndim=1]

 print(timeit(py_array_assignment, number=3)) # 0.03459 print(timeit(np_array_assignment, number=3)) # 0.00755 

Eso es con la función python también optimizada por cython. El tiempo para la función python en python puro es

 print(timeit(py_array_assignment, number=3)) # 0.12510 

Una aceleración de 17x. Claro que es un ejemplo tonto, pero pensé que era educativo.

Esto no es (solo) una adición lenta, es una sobrecarga de acceso a elementos, vea por ejemplo:

 def np_array_assignment(): np_array = np.array([1, 2]) for x in xrange(NUM_ITERATIONS): np_array[0] = 1 np_array[1] = 1 def py_array_assignment(): py_array = [1, 2] for x in xrange(NUM_ITERATIONS): py_array[0] = 1 py_array[1] = 1 timeit np_array_assignment() 10000 loops, best of 3: 178 us per loop timeit py_array_assignment() 10000 loops, best of 3: 72.5 us per loop 

Numpy es rápido al operar con vectores (matrices), cuando se realiza en toda la estructura a la vez. Tales operaciones de elemento por elemento son lentas.

Utilice las funciones numpy para evitar los bucles, realizando operaciones en toda la matriz a la vez, es decir:

 def np_array_addition_good(): np_array = np.array([1, 2]) np_array += np.sum(np.arange(NUM_ITERATIONS)) 

Los resultados que comparan tus funciones con las anteriores son bastante reveladores:

 timeit np_array_addition() 1000 loops, best of 3: 1.32 ms per loop timeit py_array_addition() 10000 loops, best of 3: 101 us per loop timeit np_array_addition_good() 100000 loops, best of 3: 11 us per loop 

Pero en realidad, puedes hacer lo mismo con python puro si colapsas los bucles:

 def py_array_addition_good(): py_array = [1, 2] rangesum = sum(range(NUM_ITERATIONS)) py_array = [x + rangesum for x in py_array] timeit py_array_addition_good() 100000 loops, best of 3: 11 us per loop 

Con todo, con operaciones tan simples, realmente no hay mejoras en el uso de Npypy. El código optimizado en python puro funciona igual de bien.


Hubo muchas preguntas al respecto y sugiero mirar algunas buenas respuestas allí:

¿Cómo maximizo la eficiencia con matrices numpy?

Número flotante: 10 veces más lento que el incorporado en operaciones aritméticas?

En realidad, no estás usando la adición de matriz vectorizada de numpy si haces el bucle en python; También está la sobrecarga de acceso mencionada por @shashkello.

Me tomé la libertad de boost un poco el tamaño de la matriz, y también agregar una versión vectorizada de la adición:

 import numpy as np from timeit import timeit NUM_ITERATIONS = 1000 def np_array_addition(): np_array = np.array(xrange(1000)) for x in xrange(NUM_ITERATIONS): for i in xrange(len(np_array)): np_array[i] += x def np_array_addition2(): np_array = np.array(xrange(1000)) for x in xrange(NUM_ITERATIONS): np_array += x def py_array_addition(): py_array = range(1000) for x in xrange(NUM_ITERATIONS): for i in xrange(len(py_array)): py_array[i] += x print timeit(np_array_addition, number=3) # 4.216162 print timeit(np_array_addition2, number=3) # 0.117681 print timeit(py_array_addition, number=3) # 0.439957 

Como puede ver, la versión numpy vectorizada gana bastante fácilmente. El espacio solo boostá a medida que aumenten los tamaños de las matrices y / o las iteraciones.