- Le blog participatif de bioinformatique francophone depuis 2012 -

Python fait la numba

Suite à cet article, j'ai eu envie de com­pa­rer les temps d'exécution de Cython et Num­ba.
Il est tou­jours inté­res­sant de faire des tests de per­for­mances (bench­marks) de temps à autre, pour voir si on ne peut pas amé­lio­rer cer­tains de nos algo­rithmes 🙂 Cython est un très bon moyen pour opti­mi­ser vos pro­grammes, mais il est encore com­pli­qué (pour moi) à appré­hen­der et néces­site de faire des efforts d'apprentissage pour accé­der à toute sa puis­sance.

Donc j'ai enten­du par­ler de Num­ba, un pro­jet encore en béta, ayant pour but de géné­rer du code machine en uti­li­sant une machine vir­tuelle à la com­pi­la­tion. Le concept est assez com­pli­qué et je vous laisse filer sur Wiki­pé­dia si vous vou­lez en savoir plus sur cette tech­nique. Ce que vous avez besoin de rete­nir pour com­prendre le concept, c'est que Num­ba uti­lise une com­pi­la­tion JIT ou Just-In-Time : à l'exécution, tra­dui­rons-nous.

Sim­ple­ment, lors de la pre­mière exé­cu­tion d'une fonc­tion que l'on veut Num­ba-ifier, Num­ba génère le code opti­mi­sé de cette fonc­tion pour votre machine, et les appels suc­ces­sifs de cette fonc­tion uti­lise alors cette der­nière ver­sion opti­mi­sé.

Comment Numba-ifier votre fonction ?

Atten­tion, ceci va être très com­pli­qué et seuls les bio­in­for­ma­ti­ciens les plus aver­tis pour­ront suivre (ne vous arré­tez pas de lire, c'est une blague…)

Ima­gi­nons que vous ayez cette fonc­tion python :

Toute simple, elle ne sert a rien à part peut être vous valo­ri­ser…

Atten­tion main­te­nant il faut suivre.
Il faut donc en pre­mier lieu impor­ter la fonc­tion de Num­ba qui nous inté­resse : "auto­jit" et la pla­cer en déco­ra­teur sur la fonc­tion.
Ce qui don­ne­ra, si vous avez sui­vi :

et … voi­là, c'est tout et c'est ce qui fait la force de Num­ba. Vous pou­vez opti­mi­ser votre code sans pra­ti­que­ment rien faire. C'est génial, sans effort, nous pou­vons opti­mi­ser une fonc­tion qui serait selon nous limi­tante pour notre pro­gramme. Et bien sûr pour les per­sonnes qui veulent vrai­ment opti­mi­ser leur code jusqu'au bout, c'est pos­sible, et à la Cython, vous pour­rez aus­si défi­nir et inter­fa­cer avec vos fonc­tions écrites en C.

Num­ba sup­porte aus­si Num­py, le code écrit en C donc, et il existe même un sup­port pré­li­mi­naire pour des appli­ca­tions CUDA. Cela reste pour l'instant expé­ri­men­tal mais cela per­met­trait de lan­cer vos cal­culs sur les GPUs de votre ordi­na­teur … Oh oui 🙂

Cepen­dant, à cette heure nous en somme à la ver­sion 0.15.1, donc une librai­rie encore en bêta et qui évo­lue­ra sur­ement encore beau­coup.

Les tests

Les tests ont été réa­li­sés sur une triple boucle for :
(Par rap­port à l'article sur Cython, IO n'a pas été tes­té car ce n'est pas encore sup­por­té par Num­ba et nous serions arri­vé à la même conclu­sion : l'accès au disque est le fac­teur limi­tant et non l'algorithme lui-même.

CC
Num­ba vs Cython | CC

On peut déjà voir que Num­ba est presque aus­si per­for­mant que Cython. Tout ça rien qu'avec un petit déco­ra­teur, et rien de plus à faire. Pas besoin de com­pi­ler d'extensions, votre code est opti­mi­sé à l'exécution, c'est d'ailleurs pour­quoi le pre­mier tour de la boucle for prend autant de temps : à ce moment là, le code n'est pas encore opti­mi­sé. Pour Cython, le code uti­li­sé est avec spé­ci­fi­ca­tion des types des variables, donc très opti­mi­sé.

Si jamais vous uti­li­sez la com­pi­la­tion sta­tique (à la Cython) vous obtien­drez un résul­tat à peu près simi­laire vu que le code aura été opti­mi­sé a prio­ri.

Atten­tion cepen­dant, Cython sera pour l'instant plus effi­cace en géné­ral : beau­coup plus de types sont sup­por­tés et la librai­rie est plus com­plète. De plus, elle a été plus éprou­vée par le temps et tes­tée par les uti­li­sa­teurs.  Mais il se peut que Num­ba devienne un concur­rent sérieux de Cython car il per­met d'avoir les même résul­tats sans avoir besoin de four­nir les même efforts. Il per­met­trait aus­si de lan­cer des cal­culs plus per­for­mant sur votre carte gra­phique. A gar­der à l’œil donc 😉

Le code de géné­ra­tion du gra­phique est dis­po­nible ici.

Mer­ci aux relec­teurs : Yoann, Syl­vain et Julien.




Commentaires

3 réponses à “Python fait la numba”

  1. Avatar de Charlie

    Salut et mer­ci pour cet article très inté­res­sant.

    Le fait qu'un code "Num­ba-ifier" ne néces­site pas une com­pi­la­tion comme en Cython être très pra­tique.

    J'ai fait quelques test sur une boucle répé­tée quelques 2000000 de fois, et le résul­tat est très impres­sion­nant.
    Seul sou­ci, je ne par­viens pas à uti­li­ser cet outil sur un objet.
    Voi­ci ci-des­sous le code que je teste en ver­sion objet (pour les pre­miers tests ayant été fait en sor­tant les fonc­tions des deux classes.

    from num­ba import auto­jit
    import time

    a = 3
    b = 2

    class Test():
    def __init__(self):
    super(Test, self).__init__()

    def somme(self):
    return a + b

    def diff(self):
    return a — b

    def multiplie(self):
    return a * b

    def divise(self):
    return a /​ b

    def compare(self):
    if a < b :
    return a
    else :
    return b

    class Test­Num­ba():
    def sommeNumba(self):
    return a + b

    def diffNumba(self):
    return a — b

    def multiplieNumba(self):
    return a * b

    def diviseNumba(self):
    return a /​ b

    def compareNumba(self):
    if a < b :
    return a
    else :
    return b

    def test():
    tps1 = time.clock()
    for i in range(2000000):
    lan­ce­Test = Test()
    lanceTest.somme()
    lanceTest.diff()
    lanceTest.multiplie()
    lanceTest.divise()
    lan​ce​Test​.com​pare()

    tps2 = time.clock()
    tps = "%.f"%((tps2 — tps1)*1000)
    return tps

    @autojit
    def testNumba(obj):
    tps1 = time.clock()
    for i in range(2000000):
    obj.sommeNumba()
    obj.diffNumba()
    obj.multiplieNumba()
    obj.diviseNumba()
    obj.compareNumba()

    tps2 = time.clock()
    tps = "%.f"%((tps2 — tps1)*1000)
    return tps

    print ('Essais en Python Simple : %s ms' % test())
    print ('Essais en Python accé­lé­ré par Num­ba : %s ms' % testNumba(TestNumba()))

    En fai­sait ain­si le code Num­ba-ifier est plus long … une astuce ???

    D'avance mer­ci.

    Char­lie

  2. Oui, Num­ba n'est pas fait pour opti­mi­ser des objets.
    D'une part quand on ne spé­ci­fie pas les types grâce à "auto­jit", Num­ba doit essayer de devi­ner ces types : num­ba types tuto­rial, ce qui peut être lent si les types sont com­plexes : des objets donc.

    D'autre part, pour l'instant, Num­ba ne sup­porte que les Types basiques, ils marquent sur leur docu­men­ta­tion ici qu'ils sont en train de déve­lop­per le sup­port de Num­ba pour des types plus com­plexes…

    En uti­li­sant, Num­ba, il faut essayer de bien sépa­rer le code exé­cu­tant les opé­ra­tions du reste du pro­gramme pour avoir un gain de temps.

  3. Nisaea_

    Mais, mais… c'est trop bien ce truc, com­ment ai-je fait pour pas­ser à côté??? Mer­ci Yo !

Laisser un commentaire