Suite à cet article, j'ai eu envie de comparer les temps d'exécution de Cython et Numba.
Il est toujours intéressant de faire des tests de performances (benchmarks) de temps à autre, pour voir si on ne peut pas améliorer certains de nos algorithmes 🙂 Cython est un très bon moyen pour optimiser vos programmes, mais il est encore compliqué (pour moi) à appréhender et nécessite de faire des efforts d'apprentissage pour accéder à toute sa puissance.
Donc j'ai entendu parler de Numba, un projet encore en béta, ayant pour but de générer du code machine en utilisant une machine virtuelle à la compilation. Le concept est assez compliqué et je vous laisse filer sur Wikipédia si vous voulez en savoir plus sur cette technique. Ce que vous avez besoin de retenir pour comprendre le concept, c'est que Numba utilise une compilation JIT ou Just-In-Time: à l'exécution, traduirons-nous.
Simplement, lors de la première exécution d'une fonction que l'on veut Numba-ifier, Numba génère le code optimisé de cette fonction pour votre machine, et les appels successifs de cette fonction utilise alors cette dernière version optimisé.
Comment Numba-ifier votre fonction ?
Attention, ceci va être très compliqué et seuls les bioinformaticiens les plus avertis pourront suivre (ne vous arrétez pas de lire, c'est une blague...)
Imaginons que vous ayez cette fonction python:
1 2 |
def mafonction(): print("Qu'elle est belle ma fonction ! Je suis vraiment quelqu'un de bien.") |
Toute simple, elle ne sert a rien à part peut être vous valoriser...
Attention maintenant il faut suivre.
Il faut donc en premier lieu importer la fonction de Numba qui nous intéresse : "autojit" et la placer en décorateur sur la fonction.
Ce qui donnera, si vous avez suivi:
1 2 3 4 5 |
from numba import autojit @autojit def mafonction(): print("Qu'elle est optimisée ma fonction ! Je suis vraiment quelqu'un de bien.") |
et ... voilà, c'est tout et c'est ce qui fait la force de Numba. Vous pouvez optimiser votre code sans pratiquement rien faire. C'est génial, sans effort, nous pouvons optimiser une fonction qui serait selon nous limitante pour notre programme. Et bien sûr pour les personnes qui veulent vraiment optimiser leur code jusqu'au bout, c'est possible, et à la Cython, vous pourrez aussi définir et interfacer avec vos fonctions écrites en C.
Numba supporte aussi Numpy, le code écrit en C donc, et il existe même un support préliminaire pour des applications CUDA. Cela reste pour l'instant expérimental mais cela permettrait de lancer vos calculs sur les GPUs de votre ordinateur ... Oh oui 🙂
Cependant, à cette heure nous en somme à la version 0.15.1, donc une librairie encore en bêta et qui évoluera surement encore beaucoup.
Les tests
Les tests ont été réalisés sur une triple boucle for:
(Par rapport à l'article sur Cython, IO n'a pas été testé car ce n'est pas encore supporté par Numba et nous serions arrivé à la même conclusion: l'accès au disque est le facteur limitant et non l'algorithme lui-même.
On peut déjà voir que Numba est presque aussi performant que Cython. Tout ça rien qu'avec un petit décorateur, et rien de plus à faire. Pas besoin de compiler d'extensions, votre code est optimisé à l'exécution, c'est d'ailleurs pourquoi le premier tour de la boucle for prend autant de temps: à ce moment là, le code n'est pas encore optimisé. Pour Cython, le code utilisé est avec spécification des types des variables, donc très optimisé.
Si jamais vous utilisez la compilation statique (à la Cython) vous obtiendrez un résultat à peu près similaire vu que le code aura été optimisé a priori.
Attention cependant, Cython sera pour l'instant plus efficace en général: beaucoup plus de types sont supportés et la librairie est plus complète. De plus, elle a été plus éprouvée par le temps et testée par les utilisateurs. Mais il se peut que Numba devienne un concurrent sérieux de Cython car il permet d'avoir les même résultats sans avoir besoin de fournir les même efforts. Il permettrait aussi de lancer des calculs plus performant sur votre carte graphique. A garder à l’œil donc 😉
Le code de génération du graphique est disponible ici.
Merci aux relecteurs: Yoann, Sylvain et Julien.
Charlie
janvier 22, 2015 à 3:17
Salut et merci pour cet article très intéressant.
Le fait qu'un code "Numba-ifier" ne nécessite pas une compilation comme en Cython être très pratique.
J'ai fait quelques test sur une boucle répétée quelques 2000000 de fois, et le résultat est très impressionnant.
Seul souci, je ne parviens pas à utiliser cet outil sur un objet.
Voici ci-dessous le code que je teste en version objet (pour les premiers tests ayant été fait en sortant les fonctions des deux classes.
from numba import autojit
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 TestNumba():
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):
lanceTest = Test()
lanceTest.somme()
lanceTest.diff()
lanceTest.multiplie()
lanceTest.divise()
lanceTest.compare()
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 Numba : %s ms' % testNumba(TestNumba()))
En faisait ainsi le code Numba-ifier est plus long ... une astuce ???
D'avance merci.
Charlie
nahoy
janvier 22, 2015 à 4:24
Oui, Numba n'est pas fait pour optimiser des objets.
D'une part quand on ne spécifie pas les types grâce à "autojit", Numba doit essayer de deviner ces types: numba types tutorial, ce qui peut être lent si les types sont complexes: des objets donc.
D'autre part, pour l'instant, Numba ne supporte que les Types basiques, ils marquent sur leur documentation ici qu'ils sont en train de développer le support de Numba pour des types plus complexes...
En utilisant, Numba, il faut essayer de bien séparer le code exécutant les opérations du reste du programme pour avoir un gain de temps.
Nisaea
mars 27, 2015 à 3:22
Mais, mais... c'est trop bien ce truc, comment ai-je fait pour passer à côté??? Merci Yo!