{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Introducción\n", "\n", "En este artículo vamos a hacer un repaso exhaustivo de **cómo acelerar sustancialmente tu código Python usando numba**. Ya hablamos sobre [la primera versión de numba](http://pybonacci.org/2012/08/21/probando-numba-compilador-para-python-basado-en-llvm/) en el blog, allá por 2012, pero ha habido importantes cambios desde entonces y la herramienta ha cambiado muchísimo. Recientemente Continuum publicó numba 0.17 con una [nueva documentación](http://numba.pydata.org/numba-doc/0.17.0/index.html) mucho más fácil de seguir, pero aun así no siempre queda claro cuál es el camino para hacer que funcione, como quedó patente con el [artículo sobre Cython](http://pybonacci.org/2015/03/09/c-elemental-querido-cython/) de Kiko. Por ello, en este artículo voy a aclarar qué puede y qué no puede hacer numba, cómo sacarle partido y voy a detallar un par de ejemplos exitosos que he producido en los últimos meses.\n", "\n", "\n", "\n", "Hablando de las nuevas versiones de numba, en su web podéis ver una [evolución temporal del rendimiento](http://numba.pydata.org/numba-benchmark/) de algunas tareas que utiliza [asv](http://spacetelescope.github.io/asv/) para la visualización." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Entendiendo numba: el modo *nopython*\n", "\n", "Como podemos leer en la documentación, [numba tiene dos modos de funcionamiento básicos](http://numba.pydata.org/numba-doc/0.17.0/user/jit.html#nopython): el modo *object* y el modo *nopython*.\n", "\n", "* El modo *object* genera código que gestiona todas las variables como objetos de Python y utiliza la API C de Python para operar con ellas. En general en este modo **no habrá ganancias de rendimiento** (e incluso puede ir más lento), con lo cual mi recomendación personal es directamente no utilizarlo. Hay casos en los que numba puede detectar los bucles y optimizarlos individualmente (*loop-jitting*), pero no le voy a prestar mucha atención a esto.\n", "* El modo *nopython* **genera código independiente de la API C de Python**. Esto tiene la desventaja de que no podemos usar todas las características del lenguaje, **pero tiene un efecto significativo en el rendimiento**. Otra de las restricciones es que **no se puede reservar memoria para objetos nuevos**.\n", "\n", "Por defecto numba usa el modo *nopython* siempre que puede, y si no pasa a modo *object*. Nosotros vamos a **forzar el modo nopython** (o «modo estricto» como me gusta llamarlo) porque es la única forma de aprovechar el potencial de numba." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ámbito de aplicación\n", "\n", "El problema del modo *nopython* es que los mensajes de error son totalmente inservibles en la mayoría de los casos, así que antes de lanzarnos a compilar funciones con numba conviene hacer un repaso de qué no podemos hacer para anticipar la mejor forma de programar nuestro código. Podéis consultar en la documentación [el subconjunto de Python soportado por numba](http://numba.pydata.org/numba-doc/0.17.0/reference/pysupported.html) en modo *nopython*, y ya os aviso que, al menos de momento, no tenemos [*list comprehensions*](https://github.com/numba/numba/issues/504), [generadores](https://github.com/numba/numba/issues/984) ni algunas cosas más. Permitidme que resalte una frase sacada de la página principal de numba:\n", "\n", "> \"*With a few annotations, **array-oriented and math-heavy Python code** can be just-in-time compiled to native machine instructions, similar in performance to C, C++ and Fortran*\". [Énfasis mío]\n", "\n", "Siento decepcionar a la audiencia pero *numba no acelerará todo el código Python* que le echemos: está enfocado a operaciones matemáticas con arrays. Aclarado este punto, vamos a ponernos manos a la obra con un ejemplo aplicado :)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Antes de empezar: instalación\n", "\n", "Puedes instalar numba en Windows, OS X y Linux con [conda](http://conda.io/) usando este comando:\n", "\n", "`conda install numba`\n", "\n", "conda se ocupará de instalar una versión correcta de LLVM, así que no tendrás que compilarla tú mismo. *Y ya está*.\n", "\n", "Ahora viene una opinión personal pero que considero importante: si eres usuario de paquetes científicos y aún no estás utilizando conda (o Anaconda) para gestionarlos, **estás en la edad de piedra**. Me declaro fanboy absoluto de Continuum Analytics por crear una herramienta de código abierto ([conda está en GitHub](http://github.com/conda/conda)) que soluciona *por fin y de una vez por todas* los problemas y frustración que hemos tenido como comunidad [desde hace 15 años](https://twitter.com/fperez_org/status/569896953875722240) y que Guido y otros se negaron a atajar. Yo llevo en esto solo desde 2011 pero aún recuerdo lo que es intentar compilar SciPy en Windows. Hazte un favor e [instala Miniconda](http://conda.pydata.org/miniconda.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Acelerando una función con numba\n", "\n", "Voy a tomar directamente el ejemplo que usó Kiko para su artículo sobre Cython y vamos a ver cómo podemos utilizar numba (y un poco de astucia) para acelerar esta función:\n", "\n", "> \"Por ejemplo, imaginemos que tenemos que detectar valores mínimos locales dentro de una malla. Los valores mínimos deberán ser simplemente valores más bajos que los que haya en los 8 nodos de su entorno inmediato. En el siguiente gráfico, el nodo en verde será un nodo con un mínimo y en su entorno son todo valores superiores:\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
(2, 0)(2, 1)(2, 2)
(1, 0)(1. 1)(1, 2)
(0, 0)(0, 1)(0, 2)
\n", "\n", "¡Vamos allá!" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Installed version_information.py. To use it, type:\n", " %load_ext version_information\n" ] } ], "source": [ "%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%load_ext version_information" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/json": { "Software versions": [ { "module": "Python", "version": "3.4.3 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]" }, { "module": "IPython", "version": "3.0.0" }, { "module": "OS", "version": "Linux 3.18.6 1 ARCH x86_64 with arch" }, { "module": "numpy", "version": "1.9.2" }, { "module": "numba", "version": "0.17.0" }, { "module": "cython", "version": "0.22" } ] }, "text/html": [ "
SoftwareVersion
Python3.4.3 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]
IPython3.0.0
OSLinux 3.18.6 1 ARCH x86_64 with arch
numpy1.9.2
numba0.17.0
cython0.22
Fri Mar 13 13:44:39 2015 CET
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", "Python & 3.4.3 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] \\\\ \\hline\n", "IPython & 3.0.0 \\\\ \\hline\n", "OS & Linux 3.18.6 1 ARCH x86\\_64 with arch \\\\ \\hline\n", "numpy & 1.9.2 \\\\ \\hline\n", "numba & 0.17.0 \\\\ \\hline\n", "cython & 0.22 \\\\ \\hline\n", "\\hline \\multicolumn{2}{|l|}{Fri Mar 13 13:44:39 2015 CET} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", "Python 3.4.3 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]\n", "IPython 3.0.0\n", "OS Linux 3.18.6 1 ARCH x86_64 with arch\n", "numpy 1.9.2\n", "numba 0.17.0\n", "cython 0.22\n", "Fri Mar 13 13:44:39 2015 CET" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%version_information numpy, numba, cython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos a empezar por importar los paquetes necesarios e inicializar la semilla del generador de números aleatorios:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "import numba\n", "\n", "np.random.seed(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Creamos nuestro array de datos:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "data = np.random.randn(2000, 2000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y voy a copiar descaradamente la función de Kiko:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def busca_min(malla):\n", " minimosx = []\n", " minimosy = []\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 1, 1, 1, ..., 1998, 1998, 1998]),\n", " array([ 1, 3, 11, ..., 1968, 1977, 1985]))" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "busca_min(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Paso 1: analizar el código\n", "\n", "Lo primero que pensé cuando vi esta función es que no me gustaba nada hacer `append` a esas dos listas tantas veces. Pero a continuación me pregunté si realmente tendrían tantos elementos... averigüémoslo:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.11091025" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mx, my = busca_min(data)\n", "mx.size / data.size" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tenemos que más de un 10 % de los elementos de la matriz cumplen la condición de ser «mínimos locales», así que no es nada despreciable. Esto en nuestro ejemplo hace *un total de más de 400 000 elementos*:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "443641" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mx.size" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ahora la idea de crear dos listas y añadir los elementos uno a uno me gusta todavía menos, así que voy a cambiar de enfoque. Lo que voy a hacer va a ser crear otro array, de la misma forma que nuestros datos, y almacenar un valor `True` en aquellos elementos que cumplan la condición de mínimo local. De esta forma cumplo también una de las reglas de oro de Software Carpentry: \"*Always initialize from data*\"." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def busca_min_np(malla):\n", " minimos = np.zeros_like(malla, dtype=bool)\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimos[i, j] = True\n", "\n", " return np.nonzero(minimos)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Encima puedo aprovechar la estupenda función `nonzero` de NumPy. Compruebo que las salidas son iguales:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "np.testing.assert_array_equal(busca_min(data)[0], busca_min_np(data)[0])\n", "np.testing.assert_array_equal(busca_min(data)[1], busca_min_np(data)[1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Y evalúo los rendimientos:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 4.75 s per loop\n" ] } ], "source": [ "%timeit busca_min(data)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 4.62 s per loop\n" ] } ], "source": [ "%timeit busca_min_np(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parece que los tiempos son más o menos parecidos, pero al menos ya no tengo dos objetos en memoria que van a crecer de manera aleatoria. Vamos a ver ahora cómo nos puede ayudar numba a acelerar este código." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Paso 2: aplicando `numba.jit(nopython=True)`\n", "\n", "Como hemos dicho antes, vamos a forzar que numba funcione en modo *nopython* para garantizar que obtenemos una mejora en el rendimiento. Si intentamos compilar la función definida en primer lugar va a fallar, porque ya hemos dicho más arriba que una de las condiciones es que *no se puede asignar memoria a objetos nuevos*:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [ { "ename": "NotImplementedError", "evalue": "Failed at nopython (nopython frontend)\n(, build_list(items=[]))", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mNotImplementedError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mbusca_min_jit\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnumba\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnopython\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbusca_min\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mbusca_min_jit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/dispatcher.py\u001b[0m in \u001b[0;36m_compile_for_args\u001b[1;34m(self, *args, **kws)\u001b[0m\n\u001b[0;32m 155\u001b[0m \u001b[1;32massert\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mkws\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 156\u001b[0m \u001b[0msig\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypeof_pyval\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0ma\u001b[0m \u001b[1;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 157\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompile\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msig\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 158\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 159\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0minspect_types\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfile\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/dispatcher.py\u001b[0m in \u001b[0;36mcompile\u001b[1;34m(self, sig)\u001b[0m\n\u001b[0;32m 275\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpy_func\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 276\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mreturn_type\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mreturn_type\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 277\u001b[1;33m flags=flags, locals=self.locals)\n\u001b[0m\u001b[0;32m 278\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 279\u001b[0m \u001b[1;31m# Check typing error if object mode is used\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mcompile_extra\u001b[1;34m(typingctx, targetctx, func, args, return_type, flags, locals, library)\u001b[0m\n\u001b[0;32m 545\u001b[0m pipeline = Pipeline(typingctx, targetctx, library,\n\u001b[0;32m 546\u001b[0m args, return_type, flags, locals)\n\u001b[1;32m--> 547\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mpipeline\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompile_extra\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 548\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 549\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mcompile_extra\u001b[1;34m(self, func)\u001b[0m\n\u001b[0;32m 291\u001b[0m \u001b[1;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 292\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 293\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompile_bytecode\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbc\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfunc_attr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunc_attr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 294\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 295\u001b[0m def compile_bytecode(self, bc, lifted=(),\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mcompile_bytecode\u001b[1;34m(self, bc, lifted, func_attr)\u001b[0m\n\u001b[0;32m 299\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlifted\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlifted\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 300\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunc_attr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfunc_attr\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 301\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_compile_bytecode\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 302\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 303\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mcompile_internal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbc\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfunc_attr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mDEFAULT_FUNCTION_ATTRIBUTES\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36m_compile_bytecode\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 532\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 533\u001b[0m \u001b[0mpm\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfinalize\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 534\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mpm\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstatus\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 535\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 536\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mrun\u001b[1;34m(self, status)\u001b[0m\n\u001b[0;32m 189\u001b[0m \u001b[1;31m# No more fallback pipelines?\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 190\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mis_final_pipeline\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 191\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mpatched_exception\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 192\u001b[0m \u001b[1;31m# Go to next fallback pipeline\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 193\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mrun\u001b[1;34m(self, status)\u001b[0m\n\u001b[0;32m 181\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mstage\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstage_name\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpipeline_stages\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mpipeline_name\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 182\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 183\u001b[1;33m \u001b[0mres\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mstage\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 184\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0m_EarlyPipelineCompletion\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 185\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mstage_nopython_frontend\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 387\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 388\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreturn_type\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 389\u001b[1;33m self.locals)\n\u001b[0m\u001b[0;32m 390\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 391\u001b[0m with self.fallback_context('Function \"%s\" has invalid return type'\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mtype_inference_stage\u001b[1;34m(typingctx, interp, args, return_type, locals)\u001b[0m\n\u001b[0;32m 662\u001b[0m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mseed_type\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mk\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mv\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 663\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 664\u001b[1;33m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbuild_constrain\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 665\u001b[0m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpropagate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 666\u001b[0m \u001b[0mtypemap\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mrestype\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcalltypes\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0munify\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36mbuild_constrain\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 375\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mblk\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mutils\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mitervalues\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mblocks\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 376\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0minst\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mblk\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbody\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 377\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconstrain_statement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 378\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 379\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mpropagate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36mconstrain_statement\u001b[1;34m(self, inst)\u001b[0m\n\u001b[0;32m 480\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mconstrain_statement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 481\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mir\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mAssign\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 482\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypeof_assign\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 483\u001b[0m \u001b[1;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mir\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mSetItem\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 484\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypeof_setitem\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36mtypeof_assign\u001b[1;34m(self, inst)\u001b[0m\n\u001b[0;32m 514\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypeof_global\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minst\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtarget\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 515\u001b[0m \u001b[1;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mir\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mExpr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 516\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypeof_expr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minst\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtarget\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 517\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 518\u001b[0m \u001b[1;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36mtypeof_expr\u001b[1;34m(self, inst, target, expr)\u001b[0m\n\u001b[0;32m 618\u001b[0m loc=inst.loc))\n\u001b[0;32m 619\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 620\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mexpr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mexpr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 621\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 622\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mtypeof_call\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0minst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtarget\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcall\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mNotImplementedError\u001b[0m: Failed at nopython (nopython frontend)\n(, build_list(items=[]))" ] } ], "source": [ "busca_min_jit = numba.jit(nopython=True)(busca_min)\n", "busca_min_jit(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En este caso la traza es inservible y especificar los tipos de entrada no va a ayudar. Solo para verificar, vamos a ver qué pasa con el rendimiento si no forzamos el modo estricto:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": true }, "outputs": [], "source": [ "busca_min_jit_object = numba.jit()(busca_min)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 4.32 s per loop\n" ] } ], "source": [ "%timeit busca_min_jit_object(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pocas ganancias respecto a la función sin compilar. ¿Qué pasa si intentamos lo mismo con la segunda función?" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": true }, "outputs": [ { "ename": "UntypedAttributeError", "evalue": "Failed at nopython (nopython frontend)\nUnknown attribute \"zeros_like\" of type Module()", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mUntypedAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mbusca_min_np_jit\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnumba\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mnopython\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbusca_min_np\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mbusca_min_np_jit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/dispatcher.py\u001b[0m in \u001b[0;36m_compile_for_args\u001b[1;34m(self, *args, **kws)\u001b[0m\n\u001b[0;32m 155\u001b[0m \u001b[1;32massert\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mkws\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 156\u001b[0m \u001b[0msig\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypeof_pyval\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0ma\u001b[0m \u001b[1;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 157\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompile\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msig\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 158\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 159\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0minspect_types\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfile\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/dispatcher.py\u001b[0m in \u001b[0;36mcompile\u001b[1;34m(self, sig)\u001b[0m\n\u001b[0;32m 275\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpy_func\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 276\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mreturn_type\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mreturn_type\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 277\u001b[1;33m flags=flags, locals=self.locals)\n\u001b[0m\u001b[0;32m 278\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 279\u001b[0m \u001b[1;31m# Check typing error if object mode is used\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mcompile_extra\u001b[1;34m(typingctx, targetctx, func, args, return_type, flags, locals, library)\u001b[0m\n\u001b[0;32m 545\u001b[0m pipeline = Pipeline(typingctx, targetctx, library,\n\u001b[0;32m 546\u001b[0m args, return_type, flags, locals)\n\u001b[1;32m--> 547\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mpipeline\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompile_extra\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 548\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 549\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mcompile_extra\u001b[1;34m(self, func)\u001b[0m\n\u001b[0;32m 291\u001b[0m \u001b[1;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 292\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 293\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompile_bytecode\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbc\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfunc_attr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunc_attr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 294\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 295\u001b[0m def compile_bytecode(self, bc, lifted=(),\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mcompile_bytecode\u001b[1;34m(self, bc, lifted, func_attr)\u001b[0m\n\u001b[0;32m 299\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlifted\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlifted\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 300\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunc_attr\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfunc_attr\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 301\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_compile_bytecode\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 302\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 303\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mcompile_internal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbc\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfunc_attr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mDEFAULT_FUNCTION_ATTRIBUTES\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36m_compile_bytecode\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 532\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 533\u001b[0m \u001b[0mpm\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfinalize\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 534\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mpm\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstatus\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 535\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 536\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mrun\u001b[1;34m(self, status)\u001b[0m\n\u001b[0;32m 189\u001b[0m \u001b[1;31m# No more fallback pipelines?\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 190\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mis_final_pipeline\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 191\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mpatched_exception\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 192\u001b[0m \u001b[1;31m# Go to next fallback pipeline\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 193\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mrun\u001b[1;34m(self, status)\u001b[0m\n\u001b[0;32m 181\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mstage\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstage_name\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpipeline_stages\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mpipeline_name\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 182\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 183\u001b[1;33m \u001b[0mres\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mstage\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 184\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0m_EarlyPipelineCompletion\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 185\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mstage_nopython_frontend\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 387\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 388\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreturn_type\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 389\u001b[1;33m self.locals)\n\u001b[0m\u001b[0;32m 390\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 391\u001b[0m with self.fallback_context('Function \"%s\" has invalid return type'\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/compiler.py\u001b[0m in \u001b[0;36mtype_inference_stage\u001b[1;34m(typingctx, interp, args, return_type, locals)\u001b[0m\n\u001b[0;32m 663\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 664\u001b[0m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbuild_constrain\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 665\u001b[1;33m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpropagate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 666\u001b[0m \u001b[0mtypemap\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mrestype\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcalltypes\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minfer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0munify\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 667\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36mpropagate\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 388\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"propagate\"\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcenter\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m80\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'-'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 389\u001b[0m \u001b[0moldtoken\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnewtoken\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 390\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconstrains\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpropagate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcontext\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtypevars\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 391\u001b[0m \u001b[0mnewtoken\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_state_token\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 392\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mconfig\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mDEBUG\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36mpropagate\u001b[1;34m(self, context, typevars)\u001b[0m\n\u001b[0;32m 110\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mconstrain\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mconstrains\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 111\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 112\u001b[1;33m \u001b[0mconstrain\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcontext\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtypevars\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 113\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mTypingError\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 114\u001b[0m \u001b[1;32mraise\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typeinfer.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, context, typevars)\u001b[0m\n\u001b[0;32m 267\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mty\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mvaltys\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 268\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 269\u001b[1;33m \u001b[0mattrty\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcontext\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mresolve_getattr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mty\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mattr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 270\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 271\u001b[0m \u001b[0margs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mattr\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mty\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0minst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typing/context.py\u001b[0m in \u001b[0;36mresolve_getattr\u001b[1;34m(self, value, attr)\u001b[0m\n\u001b[0;32m 82\u001b[0m \u001b[1;32mraise\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 83\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 84\u001b[1;33m \u001b[0mret\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mattrinfo\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mresolve\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 85\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mret\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 86\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mret\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/juanlu/.miniconda3/envs/py34/lib/python3.4/site-packages/numba/typing/templates.py\u001b[0m in \u001b[0;36mresolve\u001b[1;34m(self, value, attr)\u001b[0m\n\u001b[0;32m 241\u001b[0m \u001b[0mret\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_resolve\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 242\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mret\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 243\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mUntypedAttributeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mattr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 244\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mret\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 245\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mUntypedAttributeError\u001b[0m: Failed at nopython (nopython frontend)\nUnknown attribute \"zeros_like\" of type Module()" ] } ], "source": [ "busca_min_np_jit = numba.jit(nopython=True)(busca_min_np)\n", "busca_min_np_jit(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Me dice que no conoce la función `zeros_like`. Si acudimos a la documentación, podemos ver las [características de NumPy soportadas por numba](http://numba.pydata.org/numba-doc/0.17.0/reference/numpysupported.html) y las funciones de creación de arrays *no* figuran entre ellas. Esto es consistente con lo que hemos dicho más arriba: no vamos a poder asignar memoria a objetos nuevos." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Paso 3: Reestructurar el código\n", "\n", "¿Estamos en un callejón sin salida entonces? ¡En absoluto! Lo que vamos a hacer va a ser separar la parte intensiva de la función para aplicar `numba.jit` sobre ella, e inicializar todos los valores desde fuera. Para los que hayan usado subrutinas en Fortran este enfoque les resultará familiar :)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def busca_min_np_jit(malla):\n", " minimos = np.zeros_like(malla, dtype=bool)\n", "\n", " _busca_min(malla, minimos)\n", "\n", " return np.nonzero(minimos)\n", "\n", "@numba.jit(nopython=True)\n", "def _busca_min(malla, minimos):\n", " for i in range(1, malla.shape[1]-1):\n", " for j in range(1, malla.shape[0]-1):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimos[i, j] = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Veamos qué ocurre ahora:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(array([ 1, 1, 1, ..., 1998, 1998, 1998]),\n", " array([ 1, 3, 11, ..., 1968, 1977, 1985]))" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "busca_min_np_jit(data)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": true }, "outputs": [], "source": [ "np.testing.assert_array_equal(busca_min(data)[0], busca_min_np_jit(data)[0])\n", "np.testing.assert_array_equal(busca_min(data)[1], busca_min_np_jit(data)[1])" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 62.9 ms per loop\n" ] } ], "source": [ "%timeit busca_min_np_jit(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Habéis leído bien: **70x más rápido** :)\n", "\n", "¡Lo hemos conseguido! Ahora nuestro código funciona en numba sin problemas y encima es endemoniadamente rápido. Para completar la comparación en mi ordenador, voy a reproducir también la función hecha en Cython:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%load_ext cython" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%%cython --name probandocython9\n", "import numpy as np\n", "cimport numpy as np\n", "from cpython cimport array as c_array\n", "from array import array\n", "cimport cython\n", "\n", "@cython.boundscheck(False) \n", "@cython.wraparound(False)\n", "cpdef tuple busca_min_cython9(double [:,:] malla):\n", " cdef c_array.array minimosx, minimosy\n", " cdef unsigned int i, j\n", " cdef unsigned int ii = malla.shape[1]-1\n", " cdef unsigned int jj = malla.shape[0]-1\n", " cdef unsigned int start = 1\n", " #cdef float [:, :] malla_view = malla\n", " minimosx = array('L', [])\n", " minimosy = array('L', []) \n", " for i in range(start, ii):\n", " for j in range(start, jj):\n", " if (malla[j, i] < malla[j-1, i-1] and\n", " malla[j, i] < malla[j-1, i] and\n", " malla[j, i] < malla[j-1, i+1] and\n", " malla[j, i] < malla[j, i-1] and\n", " malla[j, i] < malla[j, i+1] and\n", " malla[j, i] < malla[j+1, i-1] and\n", " malla[j, i] < malla[j+1, i] and\n", " malla[j, i] < malla[j+1, i+1]):\n", " minimosx.append(i)\n", " minimosy.append(j)\n", "\n", " return np.array(minimosx), np.array(minimosy)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 loops, best of 3: 151 ms per loop\n" ] } ], "source": [ "%timeit busca_min_cython9(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por tanto, vemos que **la versión con numba es el doble de rápida que la versión con Cython**. Sobre gustos no hay nada escrito: yo por ejemplo valoro no «salirme» de Python usando numba mientras que a otro puede no importarle incluir especificaciones de tipos como en Cython. Los números, eso sí, son los números :)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Más casos de éxito" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### La atmósfera estándar\n", "\n", "El **cálculo de propiedades termodinámicas de la atmósfera estándar** es un problema clásico que todo aeronáutico ha afrontado alguna vez muy al principio de su carrera formativa. La teoría es simple: imponemos una ley de variación de la temperatura con la altura $T = T(h)$, la presión se obtiene por consideraciones hidrostáticas $p = p(T)$ y la densidad por la ecuación de los gases ideales $\\rho = \\rho(p, T)$. La particularidad de la atmósfera estándar es que imponemos que la variación de la temperatura con la altura es una función simplificada *y definida a trozos*, así que calcular temperatura, presión y densidad dada una altura se parece mucho a hacer esto:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "if 0.0 <= h < 11000.0:\n", " T = T0 + alpha * h\n", " p = ... # Algo que depende de T\n", " rho = p / (R_a * T)\n", "elif 11000.0 <= h < 20000.0:\n", " T = T1\n", " p = ...\n", " rho = p / (R_a * T)\n", "elif 20000.0 <= h <= 32000.0:\n", " ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "El problema viene cuando se quiere **vectorizar** esta función y permitir que `h` pueda ser un array de alturas. Esto es muy conveniente cuando queremos pintar alguna propiedad con matplotlib, por ejemplo.\n", "\n", "Se intuye que hay dos formas de hacer esto: utilizando funciones de NumPy o iterando por cada elemento del array. La primera solución se hace farragosa, y la segunda, gracias a la proverbial lentitud de Python, es extremadamente lenta. Mi amigo [Álex](http://twitter.com/Alex__S12) y yo llevamos pensando sobre este problema *años*, y nunca hemos llegado a una solución satisfactoria (incluso [encontramos algunos bugs en `numpy.piecewise`](https://github.com/numpy/numpy/pull/331) por el camino). Este año decidimos cerrar este asunto definitivamente así que con [el equipo AeroPython](https://github.com/AeroPython) exploramos varias implementaciones distintas. Hasta que por fin lo conseguimos: **usamos numba para acelerar los bucles**.\n", "\n", "![numba gana a C++](https://cloud.githubusercontent.com/assets/316517/6236738/63dabc48-b6ed-11e4-822f-2c36c0d96f76.png)\n", "\n", "Como podéis leer [en la discusión original](https://github.com/AeroPython/aeropy/issues/4#issuecomment-74748524), la función de la primera columna está escrita en C++. ¿Impresionado? ;)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Solución de Navier de una placa plana\n", "\n", "Para mi proyecto fin de carrera me encontré con la necesidad de calcular la deflexión de una placa rectangular, simplemente apoyada en sus cuatro bordes (es decir, los bordes pueden girar: no están empotrados) sometida a una carga transversal. Este problema tiene solución analítica conocida desde hace tiempo, hallada por Navier:\n", "\n", "$$w(x,y) = \\sum_{m=1}^\\infty \\sum_{n=1}^\\infty \\frac{a_{mn}}{\\pi^4 D}\\,\\left(\\frac{m^2}{a^2}+\\frac{n^2}{b^2}\\right)^{-2}\\,\\sin\\frac{m \\pi x}{a}\\sin\\frac{n \\pi y}{b}$$\n", "\n", "siendo $a_{mn}$ los coeficientes de Fourier de la carga aplicada. Como veis, para cada punto $(x, y)$ hay que hacer una doble suma en serie; si encima queremos evaluar esto en un `meshgrid`, necesitamos **un cuádruple bucle**. Ya se anticipa que por muy hábiles que estemos, a Python le va a costar.\n", "\n", "La clave estuvo, una vez más, en usar numba para optimizar los bucles. En GitHub tenéis [el código completo](https://gist.github.com/Juanlu001/cf19b1c16caf618860fb), pero la parte importante es esta:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "@numba.jit(nopython=True)\n", "def a_mn_point(P, a, b, xi, eta, mm, nn):\n", " \"\"\"Navier series coefficient for concentrated load.\n", "\n", " \"\"\"\n", " return 4 * P * sin(mm * pi * xi / a) * sin(nn * pi * eta / b) / (a * b)\n", " \n", " \n", "@numba.jit(nopython=True)\n", "def plate_displacement(xx, yy, ww, a, b, P, xi, eta, D, max_m, max_n):\n", " max_i, max_j = ww.shape\n", " for mm in range(1, max_m):\n", " for nn in range(1, max_n):\n", " for ii in range(max_i):\n", " for jj in range(max_j):\n", " a_mn = a_mn_point(P, a, b, xi, eta, mm, nn)\n", " ww[ii, jj] += (a_mn / (mm**2 / a**2 + nn**2 / b**2)**2\n", " * sin(mm * pi * xx[ii, jj] / a)\n", " * sin(nn * pi * yy[ii, jj] / b)\n", " / (pi**4 * D)) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Solución de una placa plana](https://camo.githubusercontent.com/9e60d6258ba6e0270338a8afe310db410baacfd0/68747470733a2f2f7062732e7477696d672e636f6d2f6d656469612f4235696459656843454145533470452e706e67)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podéis comprobar vosotros mismos que las diferencias de rendimiento en este caso son brutales. *Y solo hemos añadido una línea a cada función*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusiones\n", "\n", "numba aún no es una herramienta estable, pero está rápidamente alcanzando un grado de madurez suficiente para optimizar código orientado a operar con arrays. Gracias a conda es trivial de instalar y los resultados respecto a soluciones más maduras como Cython son aplastantes, tanto en velocidad de ejecución como en la complejidad del código resultante.\n", "\n", "De momento yo me quedo con numba, ¿y tú? ;)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.3" } }, "nbformat": 4, "nbformat_minor": 0 }