Optimizando tu código con Numba

19 de abril de 2021
Marta López
Marta López

Head of Marketing and Communication

Optimiza tu Código Python con Numba

Descubre todas las funcionalidades de aceleración de Python con Numba. Aprende cómo optimizar código Python y optimizar tus recursos, con esta potente herramienta.

¿Qué vamos a ver en este artículo?

Temas:

  • ¿Qué es Numba? 
  • ¿Cómo funciona Numba?
  • Primeros pasos: Compilando para la CPU
  • Usar Numba para compilar y acelerar funciones usando la CPU.

¿Qué es Numba?

Numba es un compilador para Python, el nombre del módulo diseñado especialmente para acelerar tus funciones numéricas, generando código máquina optimizado a partir de código Python, usando LLVM. Con esta herramienta puedes optimizar tu código y obtener una performance similar a la de C, y C++, sin tener que cambiar de lenguaje de programación. Importa un módulo de optimización para tu entorno de desarrollo Python.

¡Puedes ver el código en mi GitHub! 🙂

¿Cómo funciona Numba?

Numba permite acelerar código Python (las funciones numéricas) usando la CPU (Unidad Central de Procesamiento) y la GPU (Graphics Processing Unit):

  1. Compiled Functions: Numba compila solo funciones Python, no aplicaciones enteras. Básicamente, Numba es uno de los módulos de Python que mejora la performance de nuestras funciones.
  2. Just-in-time: @jit (Dynamic translation): Numba transforma el “bytecode” (código intermedio, más abstracto) a código máquina inmediatamente antes de su ejecución.
  3. Enfocado a funciones numéricas: Numba está enfocado a datos numéricos, como int, float y complex. Hoy en día, existen limitaciones para usarlo con datos de tipo string.

Numba no es la única manera de programar en CUDA, que normalmente se programa directamente en C, C++. Sin embargo, Numba te permite programar directamente en Python y optimizar tu código tanto para CPU como para GPU, simplemente cambiando un par de líneas de tu código. En relación a Python, existen otras alternativas tales como pyCUDA, aquí un resumen:

CUDA C/C++:

  1. Es la forma más común y flexible de programar en CUDA.
  2. Acelera aplicaciones en C y C++.

pyCUDA

  1. Es la forma más eficiente de programar en CUDA en Python.
  2. Requiere insertar código C en Python.

Numba

  1. Menos eficiente que pyCUDA.
  2. Te permite escribir tu código en Python.
  3. También optimiza el código para CPU.

Primeros pasos: Compilando funciones para la CPU 

Numba, a parte de acelerar tus funciones usando la GPU, puede ser usado para optimizar funciones en la CPU. Para hacerlo, simplemente usas un python decorator con lo que se denomina decorated functions.

Antes que nada, vamos a evaluar la función hiplot para comprobar cómo funciona Numba. Debemos usar el compilador @jit. El resultado es el mismo que la función de Python pura, pero Numba guarda la implementación Python original en el argumento .py_func.

GPU Benchmark 

Es importante medir la performance de nuestro código, y comprobar si Numba realmente funciona. Hay que observar una diferencia entre la implementación Python y la implementación de Numba. 

El resultado es sorprendente: ¡La función math.hypot de Python es más rápida que Numba! Esto se debe a que Numba introduce ciertos pasos cuando llamas a una función, por tanto si la función es muy simple, Numba puede hacer que tarde más que la implementación pura en Python.

Funcionamiento 

Cuando hemos inicializado la función hypot:

  • IR: Representaciones intermedias
  • Bytecode Analysis: Código intermedio, más abstracto que el código máquina.
  • LLVM: Low Level Virtual Machine, infraestructura para desarrollar compiladores.
  • NVVM: Es un compilador IR basado en LLVM, está diseñado para representar kernels en GPU.

Cada línea de Python está precedida de varias líneas de código IR. Lo más útil es ver los tipos de anotaciones que muestra Numba cuando opera con las variables, por ejemplo en pyobjectv, Numba está indicando que no conoce la función np.sin de Python y que la importa directamente del código fuente. Podemos inspeccionar los procesos para hypot usando .inspect_types().

Un Ejemplo concreto: Cómo se genera un fractal

Vamos a medir la performance de un código encargado de crear fractales usando el Conjunto de Mandelbrot y comprobar si Numba nos ayuda a optimizarlo. 

Tarda alrededor de 4.64 segundos en generar un fractal usando el Conjunto de Mandelbrot, ahora vamos a ver si Numba mejora la performance, para ello debemos usar el decorador @jit.

Podemos ver cómo hemos reducido el tiempo de 4.62 segundos a 52.4 ms… y esto ha sido hecho solo con añadir un decorador.

Un error muy común

Hemos dicho que Numba solo funciona con funciones numéricas, y aunque compila y ejecuta código Python, hay algunos tipos de datos que no puede compilar todavía (como los diccionarios).

¡¡En el ejemplo anterior vemos sin embargo cómo no ha fallado!! Hemos dicho que Numba no compila diccionarios… El punto aquí es que Numba crea 2 funciones, una para Python y otra para Numba. Por tanto, aquí estamos observando la solución de Python, podemos comprobarlo añadiendo nopython = True.

jit(nopython = True) es equivalente a njit

IMMUNE Technology Institute

Si quieres aprender Python, a desarrollar aplicaciones para el mundo del Siglo XXI, a manejar grandes cantidades de datos y diferente sistemas operativos, nuestro Máster de Data Science está diseñado para ti. No sólo adquirirás todos los conocimientos técnicos necesarios para construir tu futuro como Data Scientist, en el campus más completo e innovador de Madrid, sino que también disfrutarás descubriendo la dimensión humana de los datos. Para obtener más información, haga click aquí.

Además de ello, en nuestra institución disponemos de otros muchos cursos, entre los que destacamos el grado en ingeniería de software, donde obtendrás todos los conceptos básicos para comenzar a trabajar con Python y Numba.

Este artículo ha sido escrito por: Alejandro Díaz Santos — ( GitHub) para IMMUNE Technology Institute.

Compartir:
Volver al Blog
Suscríbete a nuestra newsletter
menuchevron-down