- Le blog participatif de bioinformatique francophone depuis 2012 -

Cython : votre programme Python mais 100x plus vite

Python est un lan­gage extrê­me­ment pra­tique car il est facile à lire et à écrire, com­pa­ré à un lan­gage de "bas niveau" et com­pi­lé comme le C. D'un autre côté, à l'exécution il est beau­coup plus lent. C'est un com­pro­mis entre les deux qu'offre Cython, per­met­tant d'accélérer votre pro­gramme d'un fac­teur 2 à plus de 100, et d'intégrer faci­le­ment des fonc­tions déjà écrites en C — d'où son nom.

Cython logo
Logo de Cython — http://​cython​.org

Un très bon exemple d'utilisation de Cython en bio­in­for­ma­tique est la librai­rie "pysam", le wrap­per Python de sam­tools (code source ici).

Cython est en fait une exten­sion du lan­gage Python, c'est-à-dire que toute com­mande Python (ou presque) est valide en Cython — mais pas l'inverse, même si on ver­ra qu'il y a moyen de ne pas tou­cher du tout au script ori­gi­nal. En pra­tique, ça signi­fie que d'habitude vous ne pou­vez plus uti­li­ser l'interpréteur ("python mons​cript​.py" vous répon­dra gen­ti­ment "Syn­tax Error") ; en contre­par­tie, vous obte­nez une traduction/​extension en C qui fait la même chose mais beau­coup plus vite. C'est donc une étape que l'on fran­chit seule­ment quand le pro­gramme fonc­tionne déjà bien et qu'on désire l'optimiser.

Soyons hon­nêtes : pas­ser à Cython ne va pas faire tour­ner tous vos pro­grammes 100 fois plus vite. Ca dépend lar­ge­ment de la struc­ture du pro­blème — de pré­fé­rence ceux avec des cal­culs com­plexes et des boucles répé­tées un grand nombre de fois — et du degré de pré­ci­sion qu'on décide d'y ajou­ter. Il ne s'agit pas non plus de trans­for­mer tout votre script, mais seule­ment une ou deux fonc­tions qui font le "gou­lot d'étranglement" au niveau de la vitesse d'exécution. Mais après un petit nombre d'ajouts bien pla­cés, on qua­druple déjà faci­le­ment.

Sa docu­men­ta­tion offi­cielle se trouve ici — en anglais. Je la trouve assez mau­vaise, c'est pour­quoi le forum offi­ciel est d'une grande aide. C'est aus­si pour­quoi j'ai écrit ce guide. Il n'est pas dif­fi­cile de trou­ver des "articles décou­verte" qui pré­sentent Cython sur le web ; ici c'est un mode d'emploi rela­ti­ve­ment com­plet que je vous pro­pose.

L'installation se fait comme d'habitude pour une librai­rie Python : "easy_​install cython" ou "pip ins­tall cython".

Table des matières :

  1. Pour vous convaincre (ou non)
  2. La ver­sion mini­male
  3. Pas­sons à Cython
  4. Le mode "pur Python"
  5. Inté­grer du C dans Python, ou l'inverse
  6. Com­plé­ment et conseils

Pour vous convaincre (ou non)

Pour éva­luer le gain appor­té par Cython dans dif­fé­rentes situa­tions, voi­ci quatre tests pour cha­cun des­quels j'ai com­pa­ré le temps d'exécution par Python, Cython com­pi­lé tel quel sans tou­cher le code (cf. sec­tion sui­vante), et Cython avec spé­ci­fi­ca­tion des types des variables.

Le pre­mier exé­cute print "Bonjour" un grand nombre de fois. Le deuxième lit ligne par ligne l'annotation du génome de C.Elegans, et compte le nombre de paires de bases codantes. Le troi­sième est une triple boucle "for" avec des opé­ra­tions mathé­ma­tiques simples. Le qua­trième est un ali­gne­ment de deux séquences avec l'algorithme de Smith-Water­man (et uti­lise Num­py). Voi­là une illus­tra­tion des temps rela­tifs :

benchmark_cython
Bench­mark pour les 4 tests, rela­ti­ve­ment au temps d'exécution par Python. De gauche à droite : print "Bon­jour", lec­ture de gros fichier, opé­ra­tions mathé­ma­tiques en boucle, ali­gne­ment de séquences. En bleu : Python ; en vert : com­pi­la­tion avec Cython seul sans typage ; en orange : avec spé­ci­fi­ca­tion des types des variables.

Pour com­men­ter ce graphe, com­men­çons par regar­der la barre verte qui montre qu'on peut obte­nir un gain signi­fi­ca­tif sans rien faire d'autre que com­pi­ler le code ori­gi­nal. Des tests 3 et 4 com­pa­rés au 1, on réa­lise que le typage fixe n'a beau­coup d'effet que sur les cal­culs numé­riques. Le deuxième montre bien que le lan­gage n'a aucun effet sur les temps d'accès en lec­ture du disque dur, res­pon­sables des trois quarts du temps mesu­ré. Le test 4 illustre com­ment Cython est opti­mi­sé pour Num­py. Dans les tests 3 et 4 le ratio entre Python et Cython est de l'ordre de 1000000:1.

Les scripts uti­li­sés sont dis­po­nibles ici. La doc offi­cielle donne aus­si d'autres exemples comme le cal­cul des nombres pre­miers, la suite de fibo­nac­ci ou l'intégration numé­rique.

La version minimale

La manière la plus simple d'accélérer sans effort un script Python, c'est de le don­ner tel quel à Cython. Rien à modi­fier donc, et un gain de vitesse d'environ 20–50% à l'exécution.

C'est aus­si utile pour com­prendre le fonc­tion­ne­ment de Cython. Il va d'abord tra­duire votre code en C, puis le com­pi­ler et le "lin­ker". A par­tir de "script​.py", vous obte­nez donc un "script.c" (la tra­duc­tion) et un "script​.so" : une col­lec­tion de fonc­tions déjà com­pi­lées et impor­tables.

Voi­ci com­ment faire, par­tant d'un fichier "script​.py" au conte­nu très simple :

1. Dans le même dos­sier que "script​.py", créer un "setup​.py" avec le conte­nu sui­vant :

2. En ligne de com­mande, tapez

ce qui va créer le .c et le .so.

3. Dans une console Python ou un nou­veau script, impor­tez et exé­cu­tez les fonc­tions fraî­che­ment com­pi­lées (pré­sentes dans le .so) :

Et voi­là, il vous répond "Bon­jour!" (mais en prin­cipe plus vite). Avec une telle fonc­tion bien sûr on ne va pas très loin… Tes­tez plu­tôt avec vos propres pro­grammes.

Remarques :

* Comme les objets "cdef" du .pyx ne sont plus impor­tables, ce sont bien celles du .so qui sont impor­tées dans "main​.py", et le "names­pace" est le même.

* Il peut y avoir de nom­breux "war­nings" lors de la com­pi­la­tion ; on peut ordon­ner au com­pi­la­teur de les igno­rer en don­nant au com­pi­la­teur les options sui­vantes (à col­ler dans le shell ou dans le .bashrc ou équi­valent) :

export CFLAGS="-Qunused-arguments ‑Wno-unu­sed-func­tion ‑Wno-unu­sed-variable"

* Pour les uti­li­sa­teurs de OSX Mave­ricks, sachez que le Xcode5 de ce der­nier est bug­gé (gcc rem­pla­cé par clang qui traite cer­tains aver­tis­se­ments bénins comme des erreurs). Il faut ajou­ter l'option sui­vante :

export ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future ;

* Pour les expé­ri­men­tés en C, on peut aus­si lan­cer sim­ple­ment "cython script​.py" et ensuite compiler/​linker soi-même à par­tir du "script.c" obte­nu.

Passons à Cython

Moti­vés par la pro­messe d'obtenir un gain de vitesse de l'ordre de 50–100x au lieu des 1.2x obte­nus ci-des­sus, inté­res­sons-nous à intro­duire Cython dans notre code. A par­tir de là, il est quand même recom­man­dé de savoir vague­ment com­ment C fonc­tionne, et ce qu'est un com­pi­la­teur.

Un pro­gramme conte­nant des direc­tives propres à Cython pren­dra l'extension ".pyx", pour bien mon­trer que ce n'est plus du Python et qu'il est inutile de lan­cer l'interpréteur des­sus. Pen­sez à le renom­mer aus­si dans "setup​.py".

Pré­ci­sons à nou­veau qu'il est inutile d'appliquer cette syn­taxe à tout votre code, mais qu'on la réser­ve­ra aux par­ties exé­cu­tées de nom­breuses fois, comme des boucles internes, et aux cal­culs mathé­ma­tiques com­plexes. Vous ne gagne­rez rien à par­ser les options de ligne de com­mande en Cython par exemple, mais beau­coup là où se trouvent les 2000 pro­duits de matrices. Toute syn­taxe Python étant valide en Cython, vous pou­vez lais­ser une grande par­tie du code inchan­gé.

Typage fixe

Avant tout il faut com­prendre que si Python est rela­ti­ve­ment lent, c'est parce que son inter­pré­teur — le truc qui trans­forme à la volée votre code en infor­ma­tion binaire pour le pro­ces­seur -, doit devi­ner tout un tas de choses que dans d'autres lan­gages on force l'utilisateur à spé­ci­fier, et sur­tout le type des variables. Pre­nons l'exemple d'une fonc­tion f(x,y)->x+y. Si on spé­ci­fie que x et y sont des entiers, l'interpréteur sau­ra 1) qu'on peut les addi­tion­ner sans pro­blème et 2) que le résul­tat est aus­si un entier. Sinon, il devra aller véri­fier toutes ces choses à chaque addi­tion — et annon­cer une erreur le cas échéant. De plus, si on connaît le type d'une variable on peut lui allouer une taille fixe de mémoire ; sinon il faut en réser­ver beau­coup plus "au cas où", et éven­tuel­le­ment l'étendre encore par la suite, d'où une perte d'efficacité.

Donc ce qui peut beau­coup accé­lé­rer un pro­gramme, c'est de spé­ci­fier le type des variables ("sta­tic typing", ou "typage fixe"), ce que Cython per­met de faire.

Pre­nons un exemple de code dans script.pyx :

On peut spé­ci­fier les types en pas­sant à

On a spé­ci­fié au com­pi­la­teur que les argu­ments a,b, la variable locale c et la valeur de retour de la fonc­tion sont des entiers, ce qui per­met­tra au pro­gramme de s'exécuter plus rapi­de­ment sans se poser de ques­tions. Par contre, à pré­sent inter­dit de don­ner autre chose que des entiers à mafonc­tion ! Notez qu'aucune des décla­ra­tions de type n'est néces­saire, et si on les enlève le code consi­dé­re­ra les variables non typées comme des "objets" dyna­miques, ce qui laisse beau­coup de flexi­bi­li­té com­pa­ré à du C.

On remarque aus­si l'utilisation des mots-clés cdef et cpdef pour défi­nir des types (C). On les étu­die­ra ci-des­sous.

Si vous repre­nez le "setup​.py" de tout à l'heure en rem­pla­çant "script​.py" par "script.pyx" dans les Exten­sions, vous pou­vez de nou­veau le com­pi­ler et impor­ter mafonc­tion dans Python.

Déclarations de types C : "cdef"

Le mot-clé cdef dit au com­pi­la­teur : "voi­ci une variable C de ce <type>", "voi­ci une fonc­tion C" ou "une struc­ture C", etc.

cdef com­prend tous les types C :
char, short, int, long, lon­glong, uchar, ushort, uint, ulong, ulon­glong,
les poin­teurs :
p_​int, pp_​int, etc.,
les valeurs boo­léennes :
bint,
et tous les types de base de Python (qu'il conver­tit) :
list, dict, tuple, str, etc.
y com­pris classes et fonc­tions.

Tout ce qu'on "cdef-init" accé­lé­re­ra le pro­gramme, mais ne sera plus acces­sible par Python. Par exemple,

devien­dra une fonc­tion C, plus per­for­mante, mais il sera impos­sible à un autre script Python de deman­der import mafonction. Pour pal­lier ce manque, il est pos­sible de décla­rer une variable ou fonc­tion avec cpdef, ce qui crée­ra à la fois une ver­sion C (mais légè­re­ment moins per­for­mante) et un wrap­per Python qu'on peut impor­ter :

Pour les classes, on uti­lise :

Ici beau­coup de choses : d'abord on déclare la classe avec cdef class. Ensuite, les attri­buts de classe sont intro­duits soit avec cdef public si on veut pou­voir modi­fier l'attribut par la suite, soit avec cdef readonly pour pou­voir seule­ment lire la valeur de l'attribut, soit juste cdef ce qui rend la variable propre aux cal­culs internes à la classe. Par exemple ici, une ins­tance M de Maclasse aura un attri­but acces­sible M.a, mais pas de M.b, uti­li­sé seule­ment au sein de la classe tout comme c.

Pour rendre mame­thode impor­table depuis Python, on la déclare avec cpdef. Si elle est uti­li­sée uni­que­ment en interne (appe­lée seule­ment à l'intérieur du même script.pyx — ou dans du code C), on peut lais­ser un simple cdef. Curieu­se­ment, les classes sont tou­jours cdef mais tou­jours acces­sibles, on pré­cise seule­ment pour leurs méthodes et variables.

On a aus­si dû chan­ger le type de self.b pour la divi­sion en ajou­tant float(), car on ne peut divi­ser un nombre à vir­gule que par autre un nombre à vir­gule. C'est là que les contraintes appa­raissent… mais tou­jours moins qu'en C.

Le construc­teur __init__ enfin, contrai­re­ment à la méthode mamethode, est décla­ré avec def et pas cdef, comme toutes les méthodes spé­ciales (avec _​_​ autour). Il existe par contre une méthode __cinit__ par­ti­cu­lière à Cython sur laquelle je ne m'étendrai pas ici. Inutile au pas­sage de re-spé­ci­fier le type des argu­ments dans la décla­ra­tion de __init__.

Pour aller plus loin

Il existe encore de nom­breuses options pour accé­lé­rer votre code. Des méthodes spé­ciales comme __cinit__, l'utilisation des malloc, calloc pour l'allo­ca­tion de mémoire, le sup­port excellent de num­py, le sup­port du paral­lé­lisme, l'utilisation des poin­teurs, les direc­tives au com­pi­la­teur (par exemple ne pas véri­fier si le déno­mi­na­teur est nul avant une divi­sion, ou si un indice peut dépas­ser la taille d'une liste), etc. Ce der­nier point en par­ti­cu­lier peut avoir un impact impor­tant, mais il faut être bien sûr de ce qu'on fait ! Si vous êtes sûr de vous, ajou­tez par exemple tout au som­met de votre pro­gramme les com­mandes

Enfin on obtient des per­for­mances opti­males en impor­tant direc­te­ment des fonc­tions C ou C++ (voir plus bas). C'est ce que fait pysam par exemple, sam­tools étant une librai­rie C.

Le mode "pur Python"

Il existe aus­si la pos­si­bi­li­té d'appliquer le typage fixe sans tou­cher du tout au script d'origine, ou tout du moins en le gar­dant lisible par l'interpréteur. Les deux pos­si­bi­li­tés sont d'augmenter le Python avec un fichier externe (.pxd) ou d'utiliser des com­mandes qui seront lues par le com­pi­la­teur C mais igno­rées par l'interpréteur.

Ce mode n'est pas véri­ta­ble­ment recom­man­dé. D'une part il se res­treint au typage fixe, ce qui passe à côté de cer­taines pos­si­bi­li­tés du lan­gage. D'autre part ce qui se passe en arrière-plan est assez flou — dif­fi­cile de savoir si ce que vous avez rajou­té dans le .pxd a bien été pris en compte. Mais on peut avoir de bonnes rai­sons de l'utiliser, comme la faci­li­té de test liée à l'interpréteur, la col­la­bo­ra­tion avec d'autres déve­lop­peurs Python, un boss qui ne veut rien savoir, etc.

Les fichiers .pxd

En uti­li­sant un fichier d'augmentation .pxd, on laisse le pro­gramme Python ori­gi­nal intact. D'un autre côté, il faut alors main­te­nir les deux fichiers en paral­lèle.

Si un fichier .pxd se trouve au même endroit qu'un fichier .py du même nom, le com­pi­la­teur va cher­cher les défi­ni­tions de type "cdef" du pre­mier et conver­tir les classes/​fonctions/​méthodes cor­res­pon­dantes dans le .py.

Donc si on a un fichier "A.py" avec

et un "A.pxd" avec

au moment de la com­pi­la­tion ce sera équi­valent à

tout en lais­sant la pos­si­bi­li­té de lan­cer "python A.py" comme avant.

Pour résu­mer ce qu'on voit en exemple, on note que pour que ça marche,

  • Les signa­tures de fonc­tions doivent être décla­rées avec cpdef::
  • Les (re-)définitions de fonc­tions doivent être décla­rées avec cpdef inline::
  • Les classes cdef doivent être décla­rées avec cdef class ;
  • Les attri­buts de classe cdef doivent être décla­rés avec cdef public ;
  • Les méthodes de classescdef doivent être décla­rées avec cpdef.
  • Les argu­ments par défaut dans le .pxd sont spé­ci­fiés avec <var>=*, comme dans
    cdef int mafonction(x, y=*).

Remarques :

* On ne peut pas fixer le type des variables locales des fonc­tions (comme dans "myfunc­tion" de l'exemple ci-des­sus). Pour ça il fau­dra uti­li­ser l'"attribut magique" @locals — voir plus loin.

* On ne peut pas aug­men­ter des fonc­tions uti­li­sant *args ou **kwargs dans le .pxd.

* Le script Python doit gar­der l'extension .py, et pas .pyx. S'il voit un .pyx il va se com­por­ter dif­fé­rem­ment — en fait de la façon nor­male, et ici ce serait plu­tôt un hack.

Les "attributs magiques" du module cython

On peut aus­si uti­li­ser, uni­que­ment ou en com­plé­ment, des fonc­tions et déco­ra­teurs spé­ciaux dis­po­nibles avec le module cython. Ces direc­tives sont igno­rées par l'interpréteur, mais pas par le com­pi­la­teur. On peut donc les uti­li­ser direc­te­ment dans le .py, bien que cela ajoute évi­dem­ment une dépen­dance au module. On peut aus­si les intro­duire dans le .pxd si on a choi­si cette voie.

On com­mence par impor­ter le module :

1. Le switch compiled :

C'est une variable spé­ciale qui vaut True quand le com­pi­la­teur tourne, et False quand c'est l'interpréteur :

2. Typage fixe :

  • cython.declare rem­place une décla­ra­tion du type cdef <type> <var> [= <valeur>] :

On peut aus­si l'utiliser pour typer les construc­teurs de classes (en par­ti­cu­lier dans un .pxd) :

  • cython.locals est un déco­ra­teur ser­vant à typer des variables locales au corps d'une fonc­tion.
  • cython.returns spé­ci­fie le type retour­né par une fonc­tion.

Il existe encore cython.cclass, cython.cfunc, cython.ccall pour décla­rer res­pec­ti­ve­ment une classe "cdef", une fonc­tion "cdef", une fonc­tion "cpdef"

Voi­là un exemple où on uti­lise un peu de tout :

Intégrer du C dans Python, ou l'inverse

Cython per­met aus­si d'interfacer du code C et du code Python de manière rela­ti­ve­ment simple. Il sup­porte aus­si com­plè­te­ment le C++.

Du C dans Python

On peut impor­ter des fonc­tions C direc­te­ment dans un module Cython. Voi­ci un exemple. Disons que vos fonc­tions C sont dans "external.h" :

Dans un "script.pyx", on redé­clare la fonc­tion comme suit :

Après com­pi­la­tion (qui aura créé un "script​.so" conte­nant un wrap­per de la fonc­tion), vous pour­rez impor­ter fonc­tionC comme n'importe quelle autre fonc­tion Python :

Du Python dans C

Si vous avez déjà un pro­gramme en C et que vous vou­lez lui ajou­ter une fonc­tion­na­li­té déjà implé­men­tée en Python, c'est pos­sible aus­si puisque la com­pi­la­tion à tra­vers Cython pro­duit direc­te­ment du code source C.

Par­tons cette fois de "script.pyx" conte­nant :

On note­ra le mot-clé public qui rend la fonc­tion acces­sible à l'import (crée un "script.h"). Main­te­nant pour l'utiliser dans un pro­gramme C "main.c" (il ne doit pas avoir le même nom puisque Cython crée déjà "script.c" !), on uti­lise cette struc­ture :

On voit qu'on a besoin de "Python.h" qui est four­ni avec Cython. Pour le trou­ver, tapez "locate Python.h" dans une console ; vous pou­vez alors soit le copier à l'endroit de votre script, soit spé­ci­fier au com­pi­la­teur où le trou­ver (avec le flag "-I").

Hon­nê­te­ment pour moi la com­pi­la­tion est tou­jours une angoisse, tout comme le C d'ailleurs, et je n'ai jamais réus­si à faire fonc­tion­ner cette par­tie. Mais si vous vous y connais­sez un peu plus, ça devrait tour­ner sans pro­blème.

Complément et conseils

Il existe des alter­na­tives à Cython, notam­ment PyPy et Num­ba pour l'accélération, weave pour l'interfaçage avec C. Plus géné­ra­le­ment, le lan­gage Julia, encore en déve­lop­pe­ment, est cen­sé résoudre com­plè­te­ment le pro­blème dans un futur proche.

Pen­sez aus­si que nom­breuses librai­ries pos­sèdent déjà des par­ties écrites en C ou en For­tran, uti­lisent déjà Cython, etc. En par­ti­cu­lier, Num­py et Sci­py sont déjà très rapides.

En ce qui concerne la bioin­fo, ce qui ralen­tit sou­vent les pro­grammes sont les mul­tiples accès au disque lors de la lecture/​écriture de fichiers, et ça, le lan­gage quel qu'il soit n'y peut rien. Il ne peut accé­lé­rer que les cal­culs (côté pro­ces­seur).

Pour ter­mi­ner, rap­pe­lons quelques bons conseils pour l'optimisation : 1. Com­men­cez par pro­duire un pro­gramme qui fonc­tionne ; seule­ment ensuite pen­sez à l'accélérer. 2. Ce sont plus pro­ba­ble­ment les défauts de votre algo­rithme qui ralen­tissent le pro­gramme, que le choix d'un lan­gage plus lent. Quand 1. et 2. sont réso­lus, pas­sez à Cython !

Mer­ci à Yoann M.bilou­web, nahoy et Nonore  pour leur relec­ture.




Commentaires

12 réponses à “Cython : votre programme Python mais 100x plus vite”

  1. Très inté­res­sant ! Mer­ci beau­coup !

  2. Avatar de Tilasoldo
    Tilasoldo

    Une superbe pré­sen­ta­tion, très détaillée et très utile ! Mer­ci !

  3. Avatar de ThomasDrummer
    ThomasDrummer

    Excel­lente pré­sen­ta­tion, com­plète et claire (en tout cas plus que les exemples offi­ciels) !
    J'ai cepen­dant un pro­blème. Je dis­pose d'un code en C que je sou­haite inté­grer dans une inter­face uti­li­sa­teur codée en Python. Le code C (NN1_1_predict.c) com­porte une fonc­tion "Com­pu­te­Pre­dic­tion".

    J'ai écrit les codes sui­vants :
    NN1_1_predict.h :

    #ifn­def NN1_​1_​PREDICT_​H
    #define NN1_​1_​PREDICT_​H
    char* ComputePrediction(double*);
    #endif

    NN1_test.pyx :

    cdef extern from "NN1_1_predict.h":
    char* ComputePrediction(double*)

    setup​.py :

    # avec setup­tools plu­tot que dis­tu­tils pour evi­ter l'erreur "Unable to find vcvarsall.bat"
    from setup­tools import setup
    from setup­tools import Exten­sion
    from Cython​.Build import cytho­nize
    from Cython.Distutils import build_​ext

    exten­sions = [Extension("NN1_test",["NN1_test.pyx"])]
    setup(cmdclass = {'build_ext':build_ext},ext_modules = cythonize(extensions))

    main​.py :

    from NN1_​test import Com­pu­te­Pre­dic­tion
    # suite du code

    Après avoir écrit "python setup​.py build_​ext –inplace" (aucun pro­blème), lorsque j'exécute "python main​.py", j'ai une erreur lors de l'importation de la fonc­tion, qui est introu­vable : "Impor­tEr­ror : can­not import name Com­pu­te­Pre­dic­tion".

    D'ailleurs si j'importe direc­te­ment "import NN1_​test" (sans erreur) et que j'exécute "dir(NN1_test)", la fonc­tion "Com­pu­te­Pre­dic­tion" n'apparaît pas.
    Si vous pou­vez m'expliquer com­ment résoudre mon pro­blème ça serait génial, ça fait plu­sieurs heures que je suis des­sus en vain ! Si j'ai bien com­pris le fonc­tion­ne­ment de Cython, je pense que le pro­blème vient de l'utilisation du "cdef" plu­tôt que "cpdef" pour la créa­tion du wrap­per python, mais intui­li­sable avec "extern".

    Mer­ci d'avance !

    1. Je ne peux pas y répondre car je n'ai jamais uti­li­sé "extern", et cette ques­tion irait mieux sur Sta­ckO­ver­flow. Mais "cpdef" sert à appe­ler la fonc­tion depuis du code Python, alors que ce qui est "cdef" n'est lisible que depuis une autre fonc­tion Cython.

  4. Avatar de ThomasDrummer
    ThomasDrummer

    Mer­ci tout de même pour la réponse rapide. Je pré­cise que j'ai uti­li­sé "extern" car c'est ce que vous sug­gé­rez dans la par­tie du tuto­riel inti­tu­lé "Du C dans Python". J'ai d'ailleurs le même pro­blème en reco­piant le code de cette par­tie :/​

  5. Avatar de ThomasDrummer
    ThomasDrummer

    Quelques jours plus tard, j'ai réus­si à résoudre mon pro­blème, du coup je me per­mets de men­tion­ner la solu­tion, et de rec­ti­fier au pas­sage une petite erreur dans le tuto­riel (en tout cas avec ma confi­gu­ra­tion), si d'autres uti­li­sa­teurs ren­contrent le même pro­blème que moi.

    Dans la sec­tion 5, pour inté­grer une fonc­tion C dans Python, il faut ajou­ter "cpdef" dans la décla­ra­tion de la fonc­tion (sinon elle n'est pas lisible par Python), de la façon sui­vante :

    cdef extern from "external.h":
    cpdef double fonctionC(double a, double b)

    Et ensuite impor­ter, par exemple dans un fichier "test​.py" :

    import script
    print script.fonctionC

    (ou avec "from script import fonc­tionC" c'est la même chose)

    Voi­là, et bonne conti­nua­tion !

  6. Avatar de Elmoussaoui
    Elmoussaoui

    Mer­ci bcp c'est très inté­res­sant.
    Par contre j'ai essayé le test "La ver­sion mini­male" ça ne marche pas quand je tape en ligne de com­mande, "python setup​.py build_​ext –inplace" il me affiche le mes­sage d'erreur sui­vant :
    error:Unable to find vcvarsall.bat

    sachant que j'ai la ver­sion python 3.5.1 avec MSC v.1900 on win32
    Mer­ci c'est vous avez idée sur ce type d'erreurs et leurs solu­tions

  7. Avatar de Elmoussaoui
    Elmoussaoui

    Mer­ci beau­coup

  8. Avatar de mimi tobler
    mimi tobler

    vous me faites mar­rer avec Cython..

    1)
    mettre dans le même package python et le pro­gramme EN python, pour aller +vite, c'est comme se tir­rer par les bottes pour mon­ter sur la lune. Si l'on fai­sait un simple chmod +x du source​.py ça irait même plus vite.
    2)
    réin­ven­ter un com­pi­la­teur NE SUFFIT PAS. Il faut un debug­ger, un édi­teur cou­plé au debug­ger et de proche en proche, un cadre de déve­lop­pe­ment — IDE, etc.
    3)
    ce n'est pas tout ! Il faut "nati­vi­ser" vers la kyrielle de OS-es, Com­pu­ters, Y‑en‑a pour bcp +de fatigue pour toute cette inten­dance que pour le simple com­pi­la­teur d'une gram­maire-syn­taxe en python.. E ce n'est pas tout, il faut réin­ven­ter un pro­gramme "libra­ry", un lin­ker, un ges­tion­naire de pro­jet et des ver­sions, etc. etc.

    OR, CES 90% d’intendance néces­saires à faire tour­ner les 10% de com­pi­la­teur seul.. ÇA EXISTE DÉJÀ.
    DEVINEZ QUOI..

    1. Avatar de Octojude
      Octojude

      Cython est une solu­tion libre à un pro­blème que l'auteur de ce com­men­taire n'a pas (s'il y en a une meilleure, autre que "reco­dez vos 100k lignes dans un autre lan­gage" à cause d'une boucle qui est trop lente, tout le monde aime­rait la voir ici en com­men­taire).
      Mon pro­gramme tourne 50x plus vite après 20 min de tra­vail sur 30 lignes de code avec Vim comme IDE. Expé­rience posi­tive >> théo­rie obs­cure (parce que hon­nê­te­ment, j'ai pas tout com­pris).
      Cela dit, il est pré­fé­rable de réflé­chir à ses besoins de per­for­mance avant de choi­sir Python pour votre pro­gramme.

      1. Très bon article mer­ci…
        "Se tirer par les bottes pour aller sur la lune, il vaut mieux faire un chmod+x" , si l'auteur de ce mes­sage pense pou­voir convaincre ceux qui lisent cette page qu'il maî­trise mieux le sujet, c'est bien le seul…

Laisser un commentaire