Acelerando Python Con Cython

Cython es un superconjunto de Python que te permite mejorar significativamente la velocidad de tu código. Puede agregar declaraciones de tipo opcionales para beneficios aún mayores. Cython convierte su código a C / C ++ optimizado que se compila en un módulo de extensión de Python. 

En este tutorial, aprenderá cómo instalar Cython, obtendrá un aumento de rendimiento inmediato de su código de Python de forma gratuita y luego cómo aprovechar realmente Cython al agregar tipos y crear un perfil de su código. Finalmente, aprenderá sobre temas más avanzados, como la integración con el código C / C ++ y NumPy, que puede explorar más para obtener aún mayores ganancias..

Contando los triples pitagóricos

Pitágoras fue un matemático y filósofo griego. Es famoso por su teorema de Pitágoras, que establece que en un triángulo rectángulo, la suma de los cuadrados de las piernas de los triángulos es igual al cuadrado de la hipotenusa. Los triples pitagóricos son cualesquiera tres enteros positivos a, byc que tales que a² + b² = c². Aquí hay un programa que encuentra todos los triples de Pitágoras cuyos miembros no son mayores que el límite provisto.

tiempo de importación def count (límite): resultado = 0 para a en rango (1, límite + 1): para b en rango (a + 1, límite + 1): para c en rango (b + 1, límite + 1) : si c * c> a * a + b * b: rompe si c * c == (a * a + b * b): resultado + = 1 resultado de retorno si __name__ == '__main__': start = time.time () result = count (1000) duration = time.time () - iniciar impresión (resultado, duración) Salida: 881 13.883624076843262 

Aparentemente hay 881 triples, y el programa tardó un poco menos de 14 segundos en descubrirlo. Eso no es demasiado largo, pero lo suficiente como para ser molesto. Si queremos encontrar más triples hasta un límite superior, debemos encontrar una manera de hacerlo más rápido. 

Resulta que hay algoritmos sustancialmente mejores, pero hoy nos estamos enfocando en hacer que Python sea más rápido con Cython, no en el mejor algoritmo para encontrar triples pitagóricos. 

Fácil Impulso Con Pyximport

La forma más fácil de usar Cython es usar la función especial de pyximport. Esta es una declaración que compila su código de Cython sobre la marcha y le permite disfrutar de los beneficios de la optimización nativa sin demasiados problemas. 

Debe poner el código a cythonize en su propio módulo, escribir una línea de configuración en su programa principal y luego importarlo como de costumbre. Vamos a ver cómo se ve. Moví la función a su propio archivo llamado pythagorean_triples.pyx. La extensión es importante para Cython. La línea que activa Cython es. importación pyximport; pyximport.install (). Luego solo importa el modulo con el contar() Función y luego la invoca en la función principal..

tiempo de importación importación pyximport; pyximport.install () importa pythagorean_triples def main (): start = time.time () result = pythagorean_triples.count (1000) duration = time.time () - inicia la impresión (resultado, duración) si __name__ == '__main__': main () Salida: 881 9.432806253433228 

La función Python pura se ejecutó un 50% más. Conseguimos este impulso mediante la adición de una sola línea. No está mal.

Construye tu propio módulo de extensión

Si bien pyximport es realmente conveniente durante el desarrollo, funciona solo en módulos de Python puros. A menudo, cuando optimiza el código, desea hacer referencia a bibliotecas C nativas o módulos de extensión de Python. 

Para admitirlos, y también para evitar la compilación dinámica en cada ejecución, puede crear su propio módulo de extensión Cython. Debe agregar un pequeño archivo setup.py y recuerde compilarlo antes de ejecutar su programa cada vez que modifique el código de Cython. Aquí está el archivo setup.py:

desde distutils.core importar la configuración desde Cython.Build importar cythonize (ext_modules = cythonize ("pythagorean_triples.pyx"))

Entonces necesitas construirlo:

$ python setup.py build_ext --inplace Compilación de pythagorean_triples.pyx porque cambió. [1/1] Cythonizing pythagorean_triples.pyx ejecutando build_ext construyendo la extensión 'pythagorean_triples' creando build creando build / temp.macosx-10.7-x86_64-3.6 gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g - fwrapv -O3 -Wall -Wstrict-prototypes -I / Users / gigi.sayfan / miniconda3 / envs / py3 / include -arch x86_64 -I / Users / gigi.sayfan / miniconda3 / envs / py3 / include -arch x86_64 -I / Usuarios / gigi.sayfan / miniconda3 / envs / py3 / include / python3.6m -c pythagorean_triples.c -o build / temp.macosx-10.7-x86_64-3.6 / pythagorean_triples.o gcc -bundle -undefined dynamic_lookup -L / Users / gigi.sayfan / miniconda3 / envs / py3 / lib -L / Users / gigi.sayfan / miniconda3 / envs / py3 / lib -arch x86_64 build / temp.macosx-10.7-x86_64-3.6 / pythagorean_triples.o -L / Users / gigi.sayfan / miniconda3 / envs / py3 / lib -o pythagorean_triples.cpython-36m-darwin.so

Como puede ver en la salida, Cython generó un archivo en C llamado pythagorean_triples.c y lo compila en un archivo .so específico de la plataforma, que es el módulo de extensión que Python ahora puede importar como cualquier otro módulo de extensión nativo.. 

Si tienes curiosidad, echa un vistazo al código C generado. Es muy largo (2789 líneas), obtuso, y contiene muchas cosas adicionales necesarias para trabajar con la API de Python. Dejemos caer el pyximport y ejecutemos nuestro programa otra vez:

importar tiempo importar pythagorean_triples def main (): start = time.time () result = pythagorean_triples.count (1000) duration = time.time () - iniciar impresión (result, duration) si __name__ == '__main__': main () 881 9.507064819335938 

El resultado es prácticamente el mismo que con pyximport. Sin embargo, tenga en cuenta que solo estoy midiendo el tiempo de ejecución del código citonizado. No estoy midiendo cuánto tiempo lleva Pyximport compilar el código citonizado sobre la marcha. En grandes programas, esto puede ser significativo..

Agregando tipos a tu código

Vamos a llevarlo al siguiente nivel. Cython es más que Python y agrega escritura opcional. Aquí, solo defino todas las variables como enteros, y el rendimiento se dispara:

# pythagorean_triples.pyx def cuenta (límite): cdef int result = 0 cdef int a = 0 cdef int b = 0 cdef int c = 0 para a en rango (1, límite + 1): para b en rango (a + 1 , límite + 1): para c en rango (b + 1, límite + 1): si c * c> a * a + b * b: break si c * c == (a * a + b * b): result + = 1 return result ---------- # main.py import time pyximport de importación; pyximport.install () importa pythagorean_triples def main (): start = time.time () result = pythagorean_triples.count (1000) duration = time.time () - inicia la impresión (resultado, duración) si __name__ == '__main__': main () Salida: 881 0.056414127349853516 

Sí. Eso es correcto. Al definir un par de enteros, el programa se ejecuta en menos de 57 milisegundos, en comparación con más de 13 segundos con Python puro. Eso es casi una mejora de 250X.

Perfilando su código

Utilicé el módulo de tiempo de Python, que mide el tiempo de pared y es bastante bueno la mayor parte del tiempo. Si desea una sincronización más precisa de los fragmentos de código pequeños, considere utilizar el módulo timeit. Aquí es cómo medir el rendimiento del código usando timeit:

>>> import timeit >>> timeit.timeit ('count (1000)', setup = "from pythagorean_triples import count", number = 1) 0.05357028398429975 # Corriendo 10 veces >>> timeit.timeit ('count (1000)' , setup = "from pythagorean_triples import count", número = 10) 0.5446877249924 

los cronométralo() La función requiere una instrucción para ejecutarse, un código de configuración que no se mide y el número de veces que se ejecuta el código medido..

Temas avanzados

Acabo de rascar la superficie aquí. Puedes hacer mucho más con Cython. Aquí hay algunos temas que pueden mejorar aún más el rendimiento de su código o permitir que Cython se integre con otros entornos:

  • llamando al código C
  • interactuando con la API de Python C y la GIL
  • usando C ++ en Python
  • portar código Cython a PyPY
  • usando paralelismo
  • Cython y NumPy
  • compartiendo declaraciones entre los módulos de Cython

Conclusión

Cython puede producir dos órdenes de magnitud de mejora de rendimiento con muy poco esfuerzo. Si desarrolla un software no trivial en Python, Cython es una obviedad. Tiene muy poca sobrecarga, y puedes introducirla gradualmente en tu base de código.

Además, no dude en ver lo que tenemos disponible para la venta y para estudiar en el mercado, y no dude en hacer cualquier pregunta y proporcionar sus valiosos comentarios utilizando la siguiente información..