¿Por qué ensum de numpy es más rápido que las funciones integradas de numpy?

Vamos a empezar con tres arrays dedtype=np.double. Los tiempos se realizan en una CPU Intel usando numpy 1.7.1 compilado conicc y vinculado a Intelmkl. Una CPU de AMD con numpy 1.6.1 compilada congcc sinmkl También se utilizó para verificar los horarios. Tenga en cuenta que la escala de tiempos es casi lineal con el tamaño del sistema y no se debe a la pequeña sobrecarga en las funciones numpyif Enunciados, estas diferencias se mostrarán en microsegundos, no en milisegundos:

arr_1D=np.arange(500,dtype=np.double)
large_arr_1D=np.arange(100000,dtype=np.double)
arr_2D=np.arange(500**2,dtype=np.double).reshape(500,500)
arr_3D=np.arange(500**3,dtype=np.double).reshape(500,500,500)

Primero veamos elnp.sum función:

np.all(np.sum(arr_3D)==np.einsum('ijk->',arr_3D))
True

%timeit np.sum(arr_3D)
10 loops, best of 3: 142 ms per loop

%timeit np.einsum('ijk->', arr_3D)
10 loops, best of 3: 70.2 ms per loop

Potestades:

np.allclose(arr_3D*arr_3D*arr_3D,np.einsum('ijk,ijk,ijk->ijk',arr_3D,arr_3D,arr_3D))
True

%timeit arr_3D*arr_3D*arr_3D
1 loops, best of 3: 1.32 s per loop

%timeit np.einsum('ijk,ijk,ijk->ijk', arr_3D, arr_3D, arr_3D)
1 loops, best of 3: 694 ms per loop

Producto exterior:

np.all(np.outer(arr_1D,arr_1D)==np.einsum('i,k->ik',arr_1D,arr_1D))
True

%timeit np.outer(arr_1D, arr_1D)
1000 loops, best of 3: 411 us per loop

%timeit np.einsum('i,k->ik', arr_1D, arr_1D)
1000 loops, best of 3: 245 us per loop

Todo lo anterior es el doble de rápido connp.einsum. Estas deben ser comparaciones de manzanas con manzanas, ya que todo es específicamente dedtype=np.double. Yo esperaría la aceleración en una operación como esta:

np.allclose(np.sum(arr_2D*arr_3D),np.einsum('ij,oij->',arr_2D,arr_3D))
True

%timeit np.sum(arr_2D*arr_3D)
1 loops, best of 3: 813 ms per loop

%timeit np.einsum('ij,oij->', arr_2D, arr_3D)
10 loops, best of 3: 85.1 ms per loop

Einsum parece ser al menos el doble de rápido paranp.inner, np.outer, np.kronynp.sum a pesar deaxes selección. La excepción primaria esnp.dot ya que llama a DGEMM desde una librería BLAS. Entonces por que esnp.einsum Más rápido que otras funciones numpy que son equivalentes?

El caso de la DGEMM para la integridad:

np.allclose(np.dot(arr_2D,arr_2D),np.einsum('ij,jk',arr_2D,arr_2D))
True

%timeit np.einsum('ij,jk',arr_2D,arr_2D)
10 loops, best of 3: 56.1 ms per loop

%timeit np.dot(arr_2D,arr_2D)
100 loops, best of 3: 5.17 ms per loop

La teoría principal es de @sebergs comentar quenp.einsum puede hacer uso deSSE2, pero los ufuncs de numpy no funcionarán hasta el numpy 1.8 (vea elregistro de cambios). Creo que esta es la respuesta correcta, pero tengono He podido confirmarlo. Se puede encontrar alguna prueba limitada cambiando el tipo de matriz de entrada y observando la diferencia de velocidad y el hecho de que no todos observan las mismas tendencias en los tiempos.

Respuestas a la pregunta(3)

Su respuesta a la pregunta