2012-06-25 22 views
11

Estoy empezando a aprender Cython debido a problemas de rendimiento. Este código en particular es un intento de implementar algunos algoritmos nuevos en el área de modelado de transporte (para planificación).¿Por qué este código es más lento en Cython que en Python?

Decidí comenzar con una función muy simple que utilizaré MUCHO (cientos de millones de veces) y definitivamente me beneficiaría de un aumento en el rendimiento.

he implementado esta función de tres maneras diferentes y las probaron para el mismo parámetro (por motivos de simplicidad) para 10 millones de veces cada uno:

  • código Cython en un módulo Cython. Tiempo de ejecución: 3.35s
  • Código de Python en un módulo de Cython. Tiempo de ejecución: 4.88s
  • código de Python en el script principal. Tiempo de ejecución: 2.98s

    Como puede ver, el código cython fue un 45% más lento que el código python en un módulo cython y un 64% más lento que el código escrito en el script principal. ¿Cómo es eso posible? ¿Dónde estoy cometiendo un error?

El código Cython es la siguiente:

def BPR2(vol, cap, al, be): 
    con=al*pow(vol/cap,be) 
    return con 


def func (float volume, float capacity,float alfa,float beta): 
    cdef float congest 
    congest=alfa*pow(volume/capacity,beta) 
    return congest 

Y el guión para la prueba es el siguiente:

agora=clock() 
for i in range(10000000): 
    q=linkdelay.BPR2(10,5,0.15,4) 

agora=clock()-agora 
print agora 

agora=clock() 
for i in range(10000000): 
    q=linkdelay.func(10,5,0.15,4) 

agora=clock()-agora 
print agora 

agora=clock() 
for i in range(10000000): 
    q=0.15*pow(10/5,4) 

agora=clock()-agora 
print agora 

Estoy al tanto de temas como funciones trascendentales (potencia) siendo más lenta, pero yo no, creo que debería ser un problema.

Dado que hay una sobrecarga para buscar la función en el espacio de funciones, ¿ayudaría el rendimiento si pasé una matriz para la función y obtuve una matriz? ¿Puedo devolver una matriz usando una función escrita en Cython?

Como referencia, estoy usando: - Windows 7 64bits - Python 2.7.3 64 Bits - Cython 0.16 64 bits - Windows Visual Studio 2008

+1

Entonces, si está pensando en pasar una matriz en la función, presumiblemente puede vectorizar el código, en cuyo caso ha considerado hacer ¿Qué estás tratando de hacer simplemente con [NumPy] (http://numpy.scipy.org/)? Ciertamente, la función en su ejemplo se puede implementar trivialmente en arreglos usando NumPy. –

+0

Bueno, es una función extremadamente trivial y cython tiene que convertir el 'PyObject *' en un flotador y luego ¿no? Parece una sobrecarga para una función tan pequeña. – Voo

+1

Solo para aclarar, su problema es que pasa la mayor parte de su tiempo llamando a la función, que no se mejora al usar Cython. Le sugiero que reformule su pregunta sin perjudicar la solución (Cython). De esa manera, aquellos que suelen contestar tendrán más para trabajar. Un pequeño ejemplo de cómo usas realmente el código sería útil. –

Respuesta

1

Esta función podría optimizarse como tal (en tanto pitón y Cython, la eliminación de la variable intermedia es más rápido):

def func(float volume, float capacity, float alfa,f loat beta): 
    return alfa * pow(volume/capacity, beta) 
+1

Eso no va a conducir exactamente al orden deseado de magnitud aumenta la velocidad ... –

+0

Pero ayudará. Pruebe esto y luego vea dónde pone la velocidad. – C0deH4cker

+5

No, no lo hará. Esta es la esencia del problema con la optimización prematura. Pon tus esfuerzos en mejoras algorítmicas. –

3

las pruebas se realizaron usando:

for i in range(10000000): 
    func(2.7,2.3,2.4,i) 

Éstos son los resultados:

cdef float func(float v, float c, float a, float b): 
    return a * (v/c) ** b 
#=> 0.85 

cpdef float func(float v, float c, float a, float b): 
    return a * (v/c) ** b 
#=> 0.84 

def func(v,c,a,b): 
    return a * pow(v/c,b) 
#=> 3.41 

cdef float func(float v, float c, float a, float b): 
    return a * pow(v/c, b) 
#=> 2.35 

Para más alta eficiencia que necesita para definir la función en C y que el tipo de retorno estática.

+0

Por curiosidad, ¿los dos últimos mejoran si 'from libc.math cimport pow'? – mgilson

+0

Ahora obtengo 3.35 y 1.35 respectivamente. Entonces sí. –

+0

Seguramente cualquier reducción de tiempo en la configuración de un tipo de retorno estático en un C lib externo será empequeñecido por la sobrecarga de llamadas. Todos los aumentos de velocidad se reducen a minimizar las llamadas a las libs de Python. Además, tengo curiosidad por saber qué está haciendo cython para que el operador '**' sea más rápido que 'pow' de' libc.math'. ¿Hay alguna posibilidad de que puedas publicar el código C entregado? –

0

Cuando Cython es más lento, es probable que se deba a conversiones de tipo, y posiblemente se vea agravado por la falta de anotaciones de tipo. Además, si usa C datastructures en Cython, tenderá a ser más rápido que usar las estructuras de datos de Python en Cython.

hice una comparación de rendimiento entre CPython 2.x (con y sin Cython, con y sin psyco), CPython 3.x (con y sin Cython), PyPy y Jython.Pypy fue de lejos el más rápido, al menos para el micro-benchmark examinado: http://stromberg.dnsalias.org/~strombrg/backshift/documentation/performance/

Cuestiones relacionadas