{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# 1A.soft - Calcul num\u00e9rique et Cython\n", "\n", "Python est tr\u00e8s lent. Il est possible d'\u00e9crire certains parties en C mais le dialogue entre les deux langages est fastidieux. Cython propose un m\u00e9lange de C et Python qui acc\u00e9l\u00e8re la conception."]}, {"cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [{"data": {"text/html": ["
run previous cell, wait for 2 seconds
\n", ""], "text/plain": [""]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["from jyquickhelper import add_notebook_menu\n", "add_notebook_menu()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Calcul num\u00e9rique\n", "\n", "On peut mesurer le temps que met en programme comme ceci (qui ne marche qu'avec [IPython...timeit](http://ipython.org/ipython-doc/dev/api/generated/IPython.core.magics.execution.html?highlight=timeit#IPython.core.magics.execution.ExecutionMagics.timeit)) :"]}, {"cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["296 \u00b5s \u00b1 16.5 \u00b5s per loop (mean \u00b1 std. dev. of 10 runs, 1,000 loops each)\n"]}], "source": ["def racine_carree1(x) :\n", " return x**0.5\n", "\n", "%timeit -r 10 [ racine_carree1(x) for x in range(0,1000) ]"]}, {"cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["260 \u00b5s \u00b1 16 \u00b5s per loop (mean \u00b1 std. dev. of 10 runs, 1,000 loops each)\n"]}], "source": ["import math\n", "def racine_carree2(x) :\n", " return math.sqrt(x)\n", "\n", "%timeit -r 10 [ racine_carree2(x) for x in range(0,1000) ]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La seconde fonction est plus rapide. Seconde v\u00e9rification :"]}, {"cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["236 \u00b5s \u00b1 36.1 \u00b5s per loop (mean \u00b1 std. dev. of 10 runs, 1,000 loops each)\n", "169 \u00b5s \u00b1 12.6 \u00b5s per loop (mean \u00b1 std. dev. of 10 runs, 10,000 loops each)\n"]}], "source": ["%timeit -r 10 [ x**0.5 for x in range(0,1000) ]\n", "%timeit -r 10 [ math.sqrt(x) for x in range(0,1000) ]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["On remarque \u00e9galement que l'appel \u00e0 une fonction pour ensuite effectuer le calcul a co\u00fbt\u00e9 environ 100 $\\mu s$ pour 1000 appels. L'instruction ``timeit`` effectue 10 boucles qui calcule 1000 fois une racine carr\u00e9e."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Cython\n", "\n", "Le module [Cython](http://cython.org/) est une fa\u00e7on d'acc\u00e9l\u00e9rer les calculs en ins\u00e9rant dans un programme python du code \u00e9crit dans une syntaxe proche de celle du C. Il existe diff\u00e9rentes approches pour acc\u00e9l\u00e9rer un programme python :\n", "\n", "- [Cython](http://cython.org/) : on ins\u00e8re du code [C](http://fr.wikipedia.org/wiki/C_(langage) dans le programme python, on peut gagn\u00e9 un facteur 10 sur des fonctions qui utilisent des boucles de fa\u00e7on intensives.\n", "- autres alternatives :\n", " - [cffi](https://cffi.readthedocs.org/), il faut conna\u00eetre le C (ne fait pas le C++)\n", " - [pythran](http://pythonhosted.org//pythran/)\n", " - [numba](https://github.com/numba/numba)\n", " - ...\n", "- [PyPy](http://pypy.org/) : on compile le programme python de fa\u00e7on statique au lieu de l'interpr\u00e9ter au fur et \u00e0 mesure de l'ex\u00e9cution, cette solution n'est praticable que si on a d\u00e9j\u00e0 programm\u00e9 avec un langage compil\u00e9 ou plus exactement un langage o\u00f9 le [typage est fort](http://en.wikipedia.org/wiki/Strong_and_weak_typing). Le langage python, parce qu'il autorise une variable \u00e0 changer de type peut cr\u00e9er des probl\u00e8mes d'[inf\u00e9rence de type](http://fr.wikipedia.org/wiki/Inf%C3%A9rence_de_types).\n", "- module impl\u00e9ment\u00e9 en C : c'est le cas le plus fr\u00e9quent et une des raisons pour lesquelles Python a \u00e9t\u00e9 rapidement adopt\u00e9. Beaucoup de librairies se sont ainsi retrouv\u00e9es disponibles en Python. N\u00e9anmoins, l'[API C](https://docs.python.org/3.4/c-api/) du Python n\u00e9cessite un investissement cons\u00e9quent pour \u00e9viter les erreurs. Il est pr\u00e9f\u00e9rable de passer par des outils tels que \n", " - [boost python](http://www.boost.org/doc/libs/1_55_0/libs/python/doc/) : facile d'acc\u00e8s, le module sera disponible sous forme compil\u00e9e,\n", " - [SWIG](http://www.swig.org/) : un peu plus difficile, le module sera soit compil\u00e9 par la librairie soit packag\u00e9 de telle sorte qu'il soit compil\u00e9 lors de son l'installation.\n", " \n", "Parmi les trois solutions, la premi\u00e8re est la plus accessible, et en d\u00e9veloppement constant ([Cython changes](https://github.com/cython/cython/blob/master/CHANGES.rst)). \n", "\n", "L'exemple qui suit ne peut pas fonctionner directement sous notebook car Cython compile un module (fichier *.pyd) avant de l'utiliser. Si la compilation ne fonctionne pas et fait appara\u00eetre un message avec ``unable for find file vcvarsall.bat``, il vous faut lire l'article [Build a Python 64 bit extension on Windows 8](http://www.xavierdupre.fr/blog/2013-07-07_nojs.html) apr\u00e8s avoir not\u00e9 la version de [Visual Studio](http://www.microsoft.com/fr-fr/download/details.aspx?id=40787) que vous utilisez. Il est pr\u00e9f\u00e9rable d'avoir programm\u00e9 en C/C++ m\u00eame si ce n'est pas indispensable."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Cython dans un notebook\n", "\n", "Le module IPython propose une fa\u00e7on simplifi\u00e9e de se servir de Cython illustr\u00e9e ici : [Some Linear Algebra with Cython](http://nbviewer.ipython.org/github/carljv/cython_testing/blob/master/cython_linalg.ipynb). Vous trouverez [plus bas](#nonb) la fa\u00e7on de faire sans IPython que nous n'utiliserons pas pour cette s\u00e9ance. On commence par les pr\u00e9liminaires \u00e0 n'ex\u00e9cuter d'une fois :"]}, {"cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": ["%load_ext cython"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Puis on d\u00e9crit la fonction avec la syntaxe Cython :"]}, {"cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [{"data": {"text/html": ["\n", "\n", "\n", "\n", " \n", " Cython: _cython_magic_1d6a283823e92ffa4c0f1198af611a6c.pyx\n", " \n", "\n", "\n", "

Generated by Cython 0.29.27

\n", "

\n", " Yellow lines hint at Python interaction.
\n", " Click on a line that starts with a \"+\" to see the C code that Cython generated for it.\n", "

\n", "
 01: cimport cython
\n", "
 02: 
\n", "
+03: def cprimes(int kmax):
\n", "
/* Python wrapper */\n", "static PyObject *__pyx_pw_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_1cprimes(PyObject *__pyx_self, PyObject *__pyx_arg_kmax); /*proto*/\n", "static PyMethodDef __pyx_mdef_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_1cprimes = {\"cprimes\", (PyCFunction)__pyx_pw_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_1cprimes, METH_O, 0};\n", "static PyObject *__pyx_pw_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_1cprimes(PyObject *__pyx_self, PyObject *__pyx_arg_kmax) {\n", "  int __pyx_v_kmax;\n", "  PyObject *__pyx_r = 0;\n", "  __Pyx_RefNannyDeclarations\n", "  __Pyx_RefNannySetupContext(\"cprimes (wrapper)\", 0);\n", "  assert(__pyx_arg_kmax); {\n", "    __pyx_v_kmax = __Pyx_PyInt_As_int(__pyx_arg_kmax); if (unlikely((__pyx_v_kmax == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error)\n", "  }\n", "  goto __pyx_L4_argument_unpacking_done;\n", "  __pyx_L3_error:;\n", "  __Pyx_AddTraceback(\"_cython_magic_1d6a283823e92ffa4c0f1198af611a6c.cprimes\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n", "  __Pyx_RefNannyFinishContext();\n", "  return NULL;\n", "  __pyx_L4_argument_unpacking_done:;\n", "  __pyx_r = __pyx_pf_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_cprimes(__pyx_self, ((int)__pyx_v_kmax));\n", "  int __pyx_lineno = 0;\n", "  const char *__pyx_filename = NULL;\n", "  int __pyx_clineno = 0;\n", "\n", "  /* function exit code */\n", "  __Pyx_RefNannyFinishContext();\n", "  return __pyx_r;\n", "}\n", "\n", "static PyObject *__pyx_pf_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_cprimes(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_kmax) {\n", "  int __pyx_v_n;\n", "  int __pyx_v_k;\n", "  int __pyx_v_i;\n", "  int __pyx_v_p[0x3E8];\n", "  PyObject *__pyx_v_result = NULL;\n", "  PyObject *__pyx_r = NULL;\n", "  __Pyx_RefNannyDeclarations\n", "  __Pyx_RefNannySetupContext(\"cprimes\", 0);\n", "/* \u2026 */\n", "  /* function exit code */\n", "  __pyx_L1_error:;\n", "  __Pyx_XDECREF(__pyx_t_1);\n", "  __Pyx_AddTraceback(\"_cython_magic_1d6a283823e92ffa4c0f1198af611a6c.cprimes\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n", "  __pyx_r = NULL;\n", "  __pyx_L0:;\n", "  __Pyx_XDECREF(__pyx_v_result);\n", "  __Pyx_XGIVEREF(__pyx_r);\n", "  __Pyx_RefNannyFinishContext();\n", "  return __pyx_r;\n", "}\n", "/* \u2026 */\n", "  __pyx_tuple_ = PyTuple_Pack(7, __pyx_n_s_kmax, __pyx_n_s_kmax, __pyx_n_s_n, __pyx_n_s_k, __pyx_n_s_i, __pyx_n_s_p, __pyx_n_s_result); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 3, __pyx_L1_error)\n", "  __Pyx_GOTREF(__pyx_tuple_);\n", "  __Pyx_GIVEREF(__pyx_tuple_);\n", "/* \u2026 */\n", "  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_1d6a283823e92ffa4c0f1198af611a6c_1cprimes, NULL, __pyx_n_s_cython_magic_1d6a283823e92ffa4c); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)\n", "  __Pyx_GOTREF(__pyx_t_1);\n", "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_cprimes, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error)\n", "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n", "
 04:     cdef int n, k, i
\n", "
 05:     cdef int p[1000]
\n", "
+06:     result = []
\n", "
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)\n", "  __Pyx_GOTREF(__pyx_t_1);\n", "  __pyx_v_result = ((PyObject*)__pyx_t_1);\n", "  __pyx_t_1 = 0;\n", "
+07:     if kmax > 1000:
\n", "
  __pyx_t_2 = ((__pyx_v_kmax > 0x3E8) != 0);\n", "  if (__pyx_t_2) {\n", "/* \u2026 */\n", "  }\n", "
+08:         kmax = 1000
\n", "
    __pyx_v_kmax = 0x3E8;\n", "
+09:     k = 0
\n", "
  __pyx_v_k = 0;\n", "
+10:     n = 2
\n", "
  __pyx_v_n = 2;\n", "
+11:     while k < kmax:
\n", "
  while (1) {\n", "    __pyx_t_2 = ((__pyx_v_k < __pyx_v_kmax) != 0);\n", "    if (!__pyx_t_2) break;\n", "
+12:         i = 0
\n", "
    __pyx_v_i = 0;\n", "
+13:         while i < k and n % p[i] != 0:
\n", "
    while (1) {\n", "      __pyx_t_3 = ((__pyx_v_i < __pyx_v_k) != 0);\n", "      if (__pyx_t_3) {\n", "      } else {\n", "        __pyx_t_2 = __pyx_t_3;\n", "        goto __pyx_L8_bool_binop_done;\n", "      }\n", "      if (unlikely((__pyx_v_p[__pyx_v_i]) == 0)) {\n", "        PyErr_SetString(PyExc_ZeroDivisionError, \"integer division or modulo by zero\");\n", "        __PYX_ERR(0, 13, __pyx_L1_error)\n", "      }\n", "      __pyx_t_3 = ((__Pyx_mod_int(__pyx_v_n, (__pyx_v_p[__pyx_v_i])) != 0) != 0);\n", "      __pyx_t_2 = __pyx_t_3;\n", "      __pyx_L8_bool_binop_done:;\n", "      if (!__pyx_t_2) break;\n", "
+14:             i = i + 1
\n", "
      __pyx_v_i = (__pyx_v_i + 1);\n", "    }\n", "
+15:         if i == k:
\n", "
    __pyx_t_2 = ((__pyx_v_i == __pyx_v_k) != 0);\n", "    if (__pyx_t_2) {\n", "/* \u2026 */\n", "    }\n", "
+16:             p[k] = n
\n", "
      (__pyx_v_p[__pyx_v_k]) = __pyx_v_n;\n", "
+17:             k = k + 1
\n", "
      __pyx_v_k = (__pyx_v_k + 1);\n", "
+18:             result.append(n)
\n", "
      __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_n); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 18, __pyx_L1_error)\n", "      __Pyx_GOTREF(__pyx_t_1);\n", "      __pyx_t_4 = __Pyx_PyList_Append(__pyx_v_result, __pyx_t_1); if (unlikely(__pyx_t_4 == ((int)-1))) __PYX_ERR(0, 18, __pyx_L1_error)\n", "      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n", "
+19:         n = n + 1
\n", "
    __pyx_v_n = (__pyx_v_n + 1);\n", "  }\n", "
+20:     return result
\n", "
  __Pyx_XDECREF(__pyx_r);\n", "  __Pyx_INCREF(__pyx_v_result);\n", "  __pyx_r = __pyx_v_result;\n", "  goto __pyx_L0;\n", "
"], "text/plain": [""]}, "execution_count": 7, "metadata": {}, "output_type": "execute_result"}], "source": ["%%cython --annotate\n", "cimport cython\n", "\n", "def cprimes(int kmax):\n", " cdef int n, k, i\n", " cdef int p[1000]\n", " result = []\n", " if kmax > 1000:\n", " kmax = 1000\n", " k = 0\n", " n = 2\n", " while k < kmax:\n", " i = 0\n", " while i < k and n % p[i] != 0:\n", " i = i + 1\n", " if i == k:\n", " p[k] = n\n", " k = k + 1\n", " result.append(n)\n", " n = n + 1\n", " return result"]}, {"cell_type": "markdown", "metadata": {}, "source": ["On termine en estimant son temps d'ex\u00e9cution. Il faut noter aussi que ce code ne peut pas \u00eatre d\u00e9plac\u00e9 dans la section pr\u00e9c\u00e9dente qui doit \u00eatre enti\u00e8rement \u00e9crite en _cython_."]}, {"cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["5.96 ms \u00b1 176 \u00b5s per loop (mean \u00b1 std. dev. of 7 runs, 100 loops each)\n"]}], "source": ["%timeit [ cprimes (567) for i in range(10) ]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Exercice : python/C appliqu\u00e9 \u00e0 une distance d'\u00e9dition\n", "\n", "La [distance de Levenshtein](http://fr.wikipedia.org/wiki/Distance_de_Levenshtein) aussi appel\u00e9 distance d'\u00e9dition calcule une distance entre deux s\u00e9quences d'\u00e9l\u00e9ments. Elle s'applique en particulier \u00e0 deux mots comme illustr\u00e9 par [Distance d'\u00e9dition et programmation dynamique](http://www.xavierdupre.fr/blog/2013-12-02_nojs.html). L'objectif est de modifier la fonction suivante de fa\u00e7on \u00e0 utiliser Cython puis de comparer les temps d'ex\u00e9cution."]}, {"cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["106 \u00b5s \u00b1 3.8 \u00b5s per loop (mean \u00b1 std. dev. of 7 runs, 10,000 loops each)\n"]}], "source": ["def distance_edition(mot1, mot2):\n", " dist = { (-1,-1): 0 }\n", " for i,c in enumerate(mot1) :\n", " dist[i,-1] = dist[i-1,-1] + 1\n", " dist[-1,i] = dist[-1,i-1] + 1\n", " for j,d in enumerate(mot2) :\n", " opt = [ ]\n", " if (i-1,j) in dist : \n", " x = dist[i-1,j] + 1\n", " opt.append(x)\n", " if (i,j-1) in dist : \n", " x = dist[i,j-1] + 1\n", " opt.append(x)\n", " if (i-1,j-1) in dist :\n", " x = dist[i-1,j-1] + (1 if c != d else 0)\n", " opt.append(x)\n", " dist[i,j] = min(opt)\n", " return dist[len(mot1)-1,len(mot2)-1]\n", "\n", "%timeit distance_edition(\"idstzance\",\"distances\")"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Auparavant, il est probablement n\u00e9cessaire de suivre ces indications :\n", "\n", "- Si vous souhaitez remplacer le dictionnaire par un tableau \u00e0 deux dimensions, comme le langage C n'autorise pas la cr\u00e9ation de tableau de longueur variables, il faut allouer un pointeur (c'est du C par du C++). Toutefois, je d\u00e9conseille cette solution :\n", " - Cython n'accepte pas les doubles pointeurs : [How to declare 2D list in Cython](http://stackoverflow.com/questions/14119254/how-to-declare-2d-list-in-cython), les pointeurs simples si [Python list to Cython](http://stackoverflow.com/questions/14780007/python-list-to-cython).\n", " - Cython n'est pas forc\u00e9ment compil\u00e9 avec la m\u00eame version que votre version du compilateur Visual Studio C++. Ce faisant, vous pourriez obtenir l'erreur ``warning C4273: 'round' : inconsistent dll linkage``. Apr\u00e8s la lecture de cet article, [BUILDING PYTHON 3.3.4 WITH VISUAL STUDIO 2013](http://p-nand-q.com/python/building-python-33-with-vs2013.html), vous comprendrez que ce n'est pas si simple \u00e0 r\u00e9soudre.\n", "\n", "Je sugg\u00e8re donc de remplacer ``dist`` par un tableau ``cdef int dist [500][500]``. La signature de la fonction est la suivante : ``def cdistance_edition(str mot1, str mot2)``. Enfin, Cython a \u00e9t\u00e9 optimis\u00e9 pour une utilisation conjointe avec ``numpy``, \u00e0 chaque fois que vous avez le choix, il vaut mieux utiliser les container numpy plut\u00f4t que d'allouer de grands tableaux sur la pile des fonctions ou d'allouer soi-m\u00eame ses propres pointeurs."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Cython sans les notebooks\n", "\n", "Cette partie n'est utile que si vous avez l'intention d'utiliser Cython sans IPython. Les lignes suivantes impl\u00e9mentent toujours avec Cython la fonction ``primes`` qui retourne les entiers premiers entiers compris entre 1 et $N$. On suit maintenant la m\u00e9thode pr\u00e9conis\u00e9e dans le [tutoriel de Cython](http://docs.cython.org/src/tutorial/cython_tutorial.html). Il faut d'abord cr\u00e9er deux fichiers :\n", "\n", "- ``example_cython.pyx`` qui contient le code de la fonction\n", "- ``setup.py`` qui compile le module avec le compilateur Visual Studio C++"]}, {"cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": ["code = \"\"\"\n", "def primes(int kmax):\n", " cdef int n, k, i\n", " cdef int p[1000]\n", " result = []\n", " if kmax > 1000:\n", " kmax = 1000\n", " k = 0\n", " n = 2\n", " while k < kmax:\n", " i = 0\n", " while i < k and n % p[i] != 0:\n", " i = i + 1\n", " if i == k:\n", " p[k] = n\n", " k = k + 1\n", " result.append(n)\n", " n = n + 1\n", " return result\n", "\"\"\"\n", "\n", "name = \"example_cython\"\n", "with open(name + \".pyx\",\"w\") as f : f.write(code)\n", "\n", "setup_code = \"\"\"\n", "from distutils.core import setup\n", "from Cython.Build import cythonize\n", "\n", "setup(\n", " ext_modules = cythonize(\"__NAME__.pyx\",\n", " compiler_directives={'language_level' : \"3\"})\n", ")\n", "\"\"\".replace(\"__NAME__\",name)\n", "\n", "with open(\"setup.py\",\"w\") as f:\n", " f.write(setup_code)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Puis on compile le fichier ``.pyx`` cr\u00e9\u00e9 en ex\u00e9cutant le fichier ``setup.py`` avec des param\u00e8tres pr\u00e9cis :"]}, {"cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [{"data": {"text/plain": ["['example_cython.c',\n", " 'example_cython.cp38-win_amd64.pyd',\n", " 'example_cython.pyx',\n", " 'setup.py',\n", " 'td1a_cython_edit.ipynb',\n", " 'td1a_cython_edit_correction.ipynb']"]}, "execution_count": 11, "metadata": {}, "output_type": "execute_result"}], "source": ["import os\n", "import sys\n", "cmd = \"{0} setup.py build_ext --inplace\".format(sys.executable)\n", "from pyquickhelper.loghelper import run_cmd\n", "out,err = run_cmd(cmd)\n", "if err != '' and err is not None: \n", " raise Exception(err)\n", " \n", "[ _ for _ in os.listdir(\".\") if \"cython\" in _ or \"setup.py\" in _ ] "]}, {"cell_type": "markdown", "metadata": {}, "source": ["Puis on importe le module :"]}, {"cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["C:\\Python395_x64\\lib\\site-packages\\Cython\\Compiler\\Main.py:369: FutureWarning: Cython directive 'language_level' not set, using 2 for now (Py2). This will change in a later release! File: C:\\xavierdupre\\__home_\\GitHub\\ensae_teaching_cs\\_doc\\notebooks\\td1a_soft\\example_cython.pyx\n", " tree = Parsing.p_module(s, pxd, full_module_name)\n"]}], "source": ["import pyximport\n", "pyximport.install()\n", "import example_cython"]}, {"cell_type": "markdown", "metadata": {}, "source": ["**Si votre derni\u00e8re modification n'appara\u00eet pas, il faut red\u00e9marrer le kernel.** Lorsque Python importe le module ``example_cython`` la premi\u00e8re fois, il charge le fichier ``example_cython.pyd``. Lors d'une modification du module, ce fichier est bloqu\u00e9 en lecture et ne peut \u00eatre modifi\u00e9. Or cela est n\u00e9cessaire car le module doit \u00eatre recompil\u00e9. Pour cette raison, il est plus pratique d'impl\u00e9menter sa fonction dans un \u00e9diteur de texte qui n'utilise pas IPython.\n", "\n", "On teste le temps mis par la fonction ``primes`` :"]}, {"cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["5.98 ms \u00b1 696 \u00b5s per loop (mean \u00b1 std. dev. of 7 runs, 100 loops each)\n"]}], "source": ["%timeit [ example_cython.primes (567) for i in range(10) ]"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Puis on compare avec la version \u00e9crites un Python :"]}, {"cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["202 ms \u00b1 23 ms per loop (mean \u00b1 std. dev. of 7 runs, 10 loops each)\n"]}], "source": ["def py_primes(kmax):\n", " p = [ 0 for _ in range(1000) ]\n", " result = []\n", " if kmax > 1000:\n", " kmax = 1000\n", " k = 0\n", " n = 2\n", " while k < kmax:\n", " i = 0\n", " while i < k and n % p[i] != 0:\n", " i = i + 1\n", " if i == k:\n", " p[k] = n\n", " k = k + 1\n", " result.append(n)\n", " n = n + 1\n", " return result\n", "\n", "%timeit [ py_primes (567) for i in range(10) ]"]}, {"cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": []}, {"cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": []}], "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.9.5"}}, "nbformat": 4, "nbformat_minor": 2}