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 :

def mafonction():
print "Bonjour!"
1. Dans le même dossier que "script.py", créer un "setup.py" avec le contenu suivant :
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

extensions = [
Extension("script", ["script.py"]) # à renommer selon les besoins
]

setup(
cmdclass = {'build_ext':build_ext},
ext_modules = cythonize(extensions),
)
2. En ligne de commande, tapez

python setup.py build_ext --inplace
ce qui va créer le .c et le .so.

3. Dans une console Python ou un nouveau script, importez et exécutez les fonctions fraîchement compilées (présentes dans le .so) :

from script import mafonction
mafonction()
Et voilà, il vous répond "Bonjour!" (mais en principe plus vite). Avec une telle fonction bien sûr on ne va pas très loin... Testez plutôt avec vos propres programmes.

Remarques :

* Comme les objets "cdef" du .pyx ne sont plus importables, ce sont bien celles du .so qui sont importées dans "main.py", et le "namespace" est le même.

* Il peut y avoir de nombreux "warnings" lors de la compilation ; on peut ordonner au compilateur de les ignorer en donnant au compilateur les options suivantes (à coller dans le shell ou dans le .bashrc ou équivalent) :

export CFLAGS="-Qunused-arguments -Wno-unused-function -Wno-unused-variable"

* Pour les utilisateurs de OSX Mavericks, sachez que le Xcode5 de ce dernier est buggé (gcc remplacé par clang qui traite certains avertissements bénins comme des erreurs). Il faut ajouter l'option suivante :

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

* Pour les expérimentés en C, on peut aussi lancer simplement "cython script.py" et ensuite compiler/linker soi-même à partir du "script.c" obtenu.

Passons à Cython

Motivés par la promesse d'obtenir un gain de vitesse de l'ordre de 50-100x au lieu des 1.2x obtenus ci-dessus, intéressons-nous à introduire Cython dans notre code. A partir de là, il est quand même recommandé de savoir vaguement comment C fonctionne, et ce qu'est un compilateur.

Un programme contenant des directives propres à Cython prendra l'extension ".pyx", pour bien montrer que ce n'est plus du Python et qu'il est inutile de lancer l'interpréteur dessus. Pensez à le renommer aussi dans "setup.py".

Précisons à nouveau qu'il est inutile d'appliquer cette syntaxe à tout votre code, mais qu'on la réservera aux parties exécutées de nombreuses fois, comme des boucles internes, et aux calculs mathématiques complexes. Vous ne gagnerez rien à parser les options de ligne de commande en Cython par exemple, mais beaucoup là où se trouvent les 2000 produits de matrices. Toute syntaxe Python étant valide en Cython, vous pouvez laisser une grande partie du code inchangé.

Typage fixe

Avant tout il faut comprendre que si Python est relativement lent, c'est parce que son interpréteur - le truc qui transforme à la volée votre code en information binaire pour le processeur -, doit deviner tout un tas de choses que dans d'autres langages on force l'utilisateur à spécifier, et surtout le type des variables. Prenons l'exemple d'une fonction f(x,y)->x+y. Si on spécifie que x et y sont des entiers, l'interpréteur saura 1) qu'on peut les additionner sans problème et 2) que le résultat est aussi un entier. Sinon, il devra aller vérifier toutes ces choses à chaque addition - et annoncer 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éserver beaucoup plus "au cas où", et éventuellement l'étendre encore par la suite, d'où une perte d'efficacité.

Donc ce qui peut beaucoup accélérer un programme, c'est de spécifier le type des variables ("static typing", ou "typage fixe"), ce que Cython permet de faire.

Prenons un exemple de code dans script.pyx :

def mafonction(a, b):
c = a - 8
return a + b + c
On peut spécifier les types en passant à
cpdef int mafonction(int a,int b):
cdef int c
c = a - 8
return a + b + c
On a spécifié au compilateur que les arguments a,b, la variable locale c et la valeur de retour de la fonction sont des entiers, ce qui permettra au programme de s'exécuter plus rapidement sans se poser de questions. Par contre, à présent interdit de donner autre chose que des entiers à mafonction ! Notez qu'aucune des déclarations de type n'est nécessaire, et si on les enlève le code considérera les variables non typées comme des "objets" dynamiques, ce qui laisse beaucoup de flexibilité comparé à du C.

On remarque aussi l'utilisation des mots-clés cdef et cpdef pour définir des types (C). On les étudiera ci-dessous.

Si vous reprenez le "setup.py" de tout à l'heure en remplaçant "script.py" par "script.pyx" dans les Extensions, vous pouvez de nouveau le compiler et importer mafonction dans Python.

Déclarations de types C: "cdef"

Le mot-clé cdef dit au compilateur : "voici une variable C de ce <type>", "voici une fonction C" ou "une structure C", etc.

cdef comprend tous les types C :
char, short, int, long, longlong, uchar, ushort, uint, ulong, ulonglong,
les pointeurs :
p_int, pp_int, etc.,
les valeurs booléennes :
bint,
et tous les types de base de Python (qu'il convertit) :
list, dict, tuple, str, etc.
y compris classes et fonctions.

Tout ce qu'on "cdef-init" accélérera le programme, mais ne sera plus accessible par Python. Par exemple,

cdef int mafonction(int a,int b):
...
deviendra une fonction C, plus performante, mais il sera impossible à un autre script Python de demander import mafonction. Pour pallier ce manque, il est possible de déclarer une variable ou fonction avec cpdef, ce qui créera à la fois une version C (mais légèrement moins performante) et un wrapper Python qu'on peut importer :
cpdef int mafonction(int a,int b):
...
Pour les classes, on utilise :
cdef class Maclasse:
cdef public int a
cdef int b
cdef double c
def __init__(self, a, b=3):
self.a = a
self.b = b
cpdef mamethode(self):
c = 3.1
print c / float(self.b)
Ici beaucoup de choses: d'abord on déclare la classe avec cdef class. Ensuite, les attributs de classe sont introduits soit avec cdef public si on veut pouvoir modifier l'attribut par la suite, soit avec cdef readonly pour pouvoir seulement lire la valeur de l'attribut, soit juste cdef ce qui rend la variable propre aux calculs internes à la classe. Par exemple ici, une instance M de Maclasse aura un attribut accessible M.a, mais pas de M.b, utilisé seulement au sein de la classe tout comme c.

Pour rendre mamethode importable depuis Python, on la déclare avec cpdef. Si elle est utilisée uniquement en interne (appelée seulement à l'intérieur du même script.pyx - ou dans du code C), on peut laisser un simple cdef. Curieusement, les classes sont toujours cdef mais toujours accessibles, on précise seulement pour leurs méthodes et variables.

On a aussi dû changer le type de self.b pour la division en ajoutant float(), car on ne peut diviser un nombre à virgule que par autre un nombre à virgule. C'est là que les contraintes apparaissent... mais toujours moins qu'en C.

Le constructeur __init__ enfin, contrairement à la méthode mamethode, est déclaré avec def et pas cdef, comme toutes les méthodes spéciales (avec __ autour). Il existe par contre une méthode __cinit__ particulière à Cython sur laquelle je ne m'étendrai pas ici. Inutile au passage de re-spécifier le type des arguments dans la déclaration de __init__.

Pour aller plus loin

Il existe encore de nombreuses options pour accélérer votre code. Des méthodes spéciales comme __cinit__, l'utilisation des malloc, calloc pour l'allocation de mémoire, le support excellent de numpy, le support du parallélisme, l'utilisation des pointeurs, les directives au compilateur (par exemple ne pas vérifier si le dénominateur est nul avant une division, ou si un indice peut dépasser la taille d'une liste), etc. Ce dernier point en particulier peut avoir un impact important, mais il faut être bien sûr de ce qu'on fait ! Si vous êtes sûr de vous, ajoutez par exemple tout au sommet de votre programme les commandes

#cython: wraparound=False
#cython: boundscheck=False
#cython: cdivision=True
Enfin on obtient des performances optimales en important directement des fonctions C ou C++ (voir plus bas). C'est ce que fait pysam par exemple, samtools étant une librairie C.

Le mode "pur Python"

Il existe aussi la possibilité d'appliquer le typage fixe sans toucher du tout au script d'origine, ou tout du moins en le gardant lisible par l'interpréteur. Les deux possibilités sont d'augmenter le Python avec un fichier externe (.pxd) ou d'utiliser des commandes qui seront lues par le compilateur C mais ignorées par l'interpréteur.

Ce mode n'est pas véritablement recommandé. D'une part il se restreint au typage fixe, ce qui passe à côté de certaines possibilités du langage. D'autre part ce qui se passe en arrière-plan est assez flou - difficile de savoir si ce que vous avez rajouté dans le .pxd a bien été pris en compte. Mais on peut avoir de bonnes raisons de l'utiliser, comme la facilité de test liée à l'interpréteur, la collaboration avec d'autres développeurs Python, un boss qui ne veut rien savoir, etc.

Les fichiers .pxd

En utilisant un fichier d'augmentation .pxd, on laisse le programme Python original intact. D'un autre côté, il faut alors maintenir les deux fichiers en parallèle.

Si un fichier .pxd se trouve au même endroit qu'un fichier .py du même nom, le compilateur va chercher les définitions de type "cdef" du premier et convertir les classes/fonctions/méthodes correspondantes dans le .py.

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

def mafonction(x, y=2):
a = x-y
return a + x * y

class A:
def __init__(self, b=0):
self.a = 3
self.b = b
def foo(self, x):
print x + 1.0
et un "A.pxd" avec

cpdef int mafonction(int x,int y=*)

cdef class A:
cdef public int a,b
cpdef foo(self, double x)
au moment de la compilation ce sera équivalent à

cpdef int mafonction(int x,int y):
a = x-y
return a + x * y

cdef class A:
cdef public int a,b
def __init__(self, b=0):
self.a = 3
self.b = b
cpdef foo(self, double x):
print x + 1.0
tout en laissant la possibilité de lancer "python A.py" comme avant.

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

  • Les signatures de fonctions doivent être déclarées avec cpdef::
cpdef int mafonction(int x,int y)
  • Les (re-)définitions de fonctions doivent être déclarées avec cpdef inline::
cpdef inline int mafonction(int x,int y):
pass
  • Les classes cdef doivent être déclarées avec cdef class;
  • Les attributs de classe cdef doivent être déclarés avec cdef public;
  • Les méthodes de classescdef doivent être déclarées avec cpdef.
  • Les arguments par défaut dans le .pxd sont spécifiés avec <var>=*, comme dans
    cdef int mafonction(x, y=*).

 

Remarques :

* On ne peut pas fixer le type des variables locales des fonctions (comme dans "myfunction" de l'exemple ci-dessus). Pour ça il faudra utiliser l'"attribut magique" @locals - voir plus loin.

* On ne peut pas augmenter des fonctions utilisant *args ou **kwargs dans le .pxd.

* Le script Python doit garder l'extension .py, et pas .pyx. S'il voit un .pyx il va se comporter différemment - en fait de la façon normale, et ici ce serait plutôt un hack.

Les "attributs magiques" du module cython

On peut aussi utiliser, uniquement ou en complément, des fonctions et décorateurs spéciaux disponibles avec le module cython. Ces directives sont ignorées par l'interpréteur, mais pas par le compilateur. On peut donc les utiliser directement dans le .py, bien que cela ajoute évidemment une dépendance au module. On peut aussi les introduire dans le .pxd si on a choisi cette voie.

On commence par importer le module :

import cython
1. Le switch compiled :
C'est une variable spéciale qui vaut True quand le compilateur tourne, et False quand c'est l'interpréteur :
if cython.compiled:
print("Je suis compilé en C.")
else:
print("Je suis un bête script Python.")
2. Typage fixe :
  • cython.declare remplace une déclaration du type cdef <type> <var> [= <valeur>] :
cython.declare(x=cython.int, y=cython.double)   # -> cdef int x; cdef double y
On peut aussi l'utiliser pour typer les constructeurs de classes (en particulier dans un .pxd) :
class A:
cython.declare(a=cython.int, b=cython.int)
def __init__(self, b=0):
self.a = 3
self.b = b
  • cython.locals est un décorateur servant à typer des variables locales au corps d'une fonction.
  • cython.returns spécifie le type retourné par une fonction.

Il existe encore cython.cclass, cython.cfunc, cython.ccall pour déclarer respectivement une classe "cdef", une fonction "cdef", une fonction "cpdef"

Voilà un exemple où on utilise un peu de tout :

@cython.cfunc
@cython.returns(cython.bint)
@cython.locals(a=cython.int, b=cython.int)
def compare(a,b):
return a == b

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

Cython permet aussi d'interfacer du code C et du code Python de manière relativement simple. Il supporte aussi complètement le C++.

Du C dans Python

On peut importer des fonctions C directement dans un module Cython. Voici un exemple. Disons que vos fonctions C sont dans "external.h" :

#include <stdio.h>
#include <stdlib.h>

double fonctionC(double a, double b){
return a*b;
}
Dans un "script.pyx", on redéclare la fonction comme suit :

cdef extern from "external.h":
double fonctionC(double a, double b)
Après compilation (qui aura créé un "script.so" contenant un wrapper de la fonction), vous pourrez importer fonctionC comme n'importe quelle autre fonction Python :
import script
print fonctionC(21,24)

Du Python dans C

Si vous avez déjà un programme en C et que vous voulez lui ajouter une fonctionnalité déjà implémentée en Python, c'est possible aussi puisque la compilation à travers Cython produit directement du code source C.

Partons cette fois de "script.pyx" contenant :

cdef public mafonction(double a,double b):
return a*b
On notera le mot-clé public qui rend la fonction accessible à l'import (crée un "script.h"). Maintenant pour l'utiliser dans un programme C "main.c" (il ne doit pas avoir le même nom puisque Cython crée déjà "script.c" !), on utilise cette structure :
#include <stdio.h>
#include <stdlib.h>

#include <Python.h>
#include "script.h" // wrapper Cython de vos fonctions

int main(){
Py_Initialize();
result mafonction(345, 765);
printf("Resultat : %i\n", result);
Py_Finalize();
}
On voit qu'on a besoin de "Python.h" qui est fourni avec Cython. Pour le trouver, tapez "locate Python.h" dans une console ; vous pouvez alors soit le copier à l'endroit de votre script, soit spécifier au compilateur où le trouver (avec le flag "-I").

Honnêtement pour moi la compilation est toujours une angoisse, tout comme le C d'ailleurs, et je n'ai jamais réussi à faire fonctionner cette partie. Mais si vous vous y connaissez un peu plus, ça devrait tourner sans problème.

Complément et conseils

Il existe des alternatives à Cython, notamment PyPy et Numba pour l'accélération, weave pour l'interfaçage avec C. Plus généralement, le langage Julia, encore en développement, est censé résoudre complètement le problème dans un futur proche.

Pensez aussi que nombreuses librairies possèdent déjà des parties écrites en C ou en Fortran, utilisent déjà Cython, etc. En particulier, Numpy et Scipy sont déjà très rapides.

En ce qui concerne la bioinfo, ce qui ralentit souvent les programmes sont les multiples accès au disque lors de la lecture/écriture de fichiers, et ça, le langage quel qu'il soit n'y peut rien. Il ne peut accélérer que les calculs (côté processeur).

Pour terminer, rappelons quelques bons conseils pour l'optimisation : 1. Commencez par produire un programme qui fonctionne ; seulement ensuite pensez à l'accélérer. 2. Ce sont plus probablement les défauts de votre algorithme qui ralentissent le programme, que le choix d'un langage plus lent. Quand 1. et 2. sont résolus, passez à Cython !

 

Merci à Yoann M.bilouweb, nahoy et Nonore  pour leur relecture.

 



Pour continuer la lecture :


Commentaires

11 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.

Laisser un commentaire