2012-05-24 12 views
7

Necesito implementar una función para sumar los elementos de una matriz con una longitud de sección variable. Así,cython numpy accumulate function

a = np.arange(10) 
section_lengths = np.array([3, 2, 4]) 
out = accumulate(a, section_lengths) 
print out 
array([ 3., 7., 35.]) 

que intentaron una implementación en cython aquí:

https://gist.github.com/2784725

para un rendimiento estoy comparando a la solución pura numpy para el caso en que los section_lengths son todos iguales:

LEN = 10000 
b = np.ones(LEN, dtype=np.int) * 2000 
a = np.arange(np.sum(b), dtype=np.double) 
out = np.zeros(LEN, dtype=np.double) 

%timeit np.sum(a.reshape(-1,2000), axis=1) 
10 loops, best of 3: 25.1 ms per loop 

%timeit accumulate.accumulate(a, b, out) 
10 loops, best of 3: 64.6 ms per loop 

¿Tiene alguna sugerencia para mejorar el rendimiento?

+0

he implementado varias sugerencias, ver la versión actualizada en github: https://gist.github.com/2784725/8e2aaebbaa68c67e7a0686e9c7927f2f5b6f419a, todavía se tarda 63ms, por lo que no significativa mejora –

+2

Podría estar fuera del punto, pero pensé en mencionar ... numpy ya tiene algo parecido a * all * ufuncs. 'np.add.reduceat (a, section_lengths.cumsum())'. Tiene que ser cambiado un poco (cumsum no tiene un 0 al inicio y se obtiene el segmento final extra) y probablemente puedas vencer la velocidad con cython, pero es una característica/truco muy bueno. – seberg

Respuesta

2

Es posible probar algunos de los siguientes:

  • Además de la directiva @cython.boundscheck(False) compilador, también tratan de añadir @cython.wraparound(False)

  • En su script setup.py, trate de añadir en algunos parámetros de optimización:

    ext_modules = [Extension("accumulate", ["accumulate.pyx"], extra_compile_args=["-O3",])]

  • Tome un vistazo al archivo html generado por cython -a accumulate.pyx para ver si hay secciones que faltan tipos estáticos o dependen en gran medida de Python llamadas de la API C:

    http://docs.cython.org/src/quickstart/cythonize.html#determining-where-to-add-types

  • Añadir un comunicado al final return del método. Actualmente está haciendo un montón de comprobaciones de errores innecesarios en su circuito cerrado al i_el += 1.

  • No está seguro de si va a hacer una diferencia pero tienden a hacer contadores de bucles cdef unsigned int en lugar de sólo int

También puede comparar su código para numpy cuando section_lengths son desiguales, ya que probablemente requerirá un poco más que un simple sum.

+0

gracias! Implementé todas sus sugerencias, pero aún así no hay una mejora significativa. Gracias por sugerir cython -a, no sabía de eso. Agregué una declaración de devolución, que muestra algunas comprobaciones extrañas que está haciendo el código, vea https: //gist.github.com/2784725 # gistcomment-330807 –

+0

Estoy aceptando esta respuesta porque brinda sugerencias útiles, pero ninguna de ellas mejora significativamente. Cambiaré la respuesta aceptada en caso de que alguien más encuentre algo mejor. –

1

En el nido para la actualización de bucle out[i_bas] es lento, puede crear una variable temporal para hacer el accumerate y actualizar out[i_bas] cuando finalice el ciclo nido. El siguiente código será tan rápido como la versión numpy:

import numpy as np 
cimport numpy as np 

ctypedef np.int_t DTYPE_int_t 
ctypedef np.double_t DTYPE_double_t 

cimport cython 
@cython.boundscheck(False) 
@cython.wraparound(False) 
def accumulate(
     np.ndarray[DTYPE_double_t, ndim=1] a not None, 
     np.ndarray[DTYPE_int_t, ndim=1] section_lengths not None, 
     np.ndarray[DTYPE_double_t, ndim=1] out not None, 
     ): 
    cdef int i_el, i_bas, sec_length, lenout 
    cdef double tmp 
    lenout = out.shape[0] 
    i_el = 0 
    for i_bas in range(lenout): 
     tmp = 0 
     for sec_length in range(section_lengths[i_bas]): 
      tmp += a[i_el] 
      i_el+=1 
     out[i_bas] = tmp 
+0

gracias! seguí tu sugerencia pero no hay una mejora significativa, actualicé mi versión en github –

Cuestiones relacionadas