Vitesse d'exécution code Python et dérivé par rapport au C++

Sous-titre

Auteur

Charlie Gentil

L'article qui suit doit permettre au développeur d'orienter son choix vers une solution lui permettant d'améliorer les performances de ses programmes.

- Titre : Vitesse d'exécution code Python et dérivé par rapport au C++

- Auteur : Charlie Gentil

- Parution : 3 janvier 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2014 Charlie Gentil. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Création des codes

- II-A. Code Python

- II-B. Code Cython

- II-C. Code QML + divers appels

- II-C-1. Code QML

- II-C-2. Appel via Python

- II-C-3. Appel via Cython

- II-C-4. Appel via C++

- II-D. Code C++

- III. Tests des codes créés

- IV. Conclusion

- V. Remerciements

I - Introduction

Nous allons voir dans cet article que le temps d'exécution d'un code Python peut parfois être relativement long et qu'il existe différentes manières pour y remédier.

Cet article n'a pas pour but d'expliquer en détail comment le développeur peut faire ceci. Il s'agit juste ici d'une simple présentation associée à une comparaison avec les performances d'un code écrit en C++.

Comme code exemple nous choisirons celui du plus grand diviseur commun entre deux nombres.

Pour ce faire nous réaliserons ce code dans les configurations suivantes :

- en pur Python ;

- en Cython ;

- en QML appelé par Python ;

- en QML appelé par Cython ;

- en QML appelé par C++ ;

- en pur C++ ;

Comme vu plus haut, le but de cet article n'est pas d'expliquer comment sont créés les codes, mais uniquement de faire les tests nécessaires. Ne cherchez donc pas ici un tutoriel sur le langage Cython ou QML par exemple.

II - Création des codes

Ci-dessous vous trouverez les différentes versions du code que nous utiliserons. Afin d'augmenter volontairement le temps d'exécution nous allons faire en sorte de répéter l'opération plusieurs centaines de fois.

II-A - Code Python

codepython.py

def fonction(a,b):
while b != 0:
a,b=b,a%b
return a

for i in range(0,10000001):
fonction(9078,5118)

print("fin du test i = ", i)

II-B - Code Cython

codecython.pyx

cpdef fonction(a,b):
while b != 0:
a,b=b,a%b
return a

for i in range(0,10000001):
fonction(9078,5118)

print("fin du test i = ", i)

II-C - Code QML + divers appels

II-C-1 - Code QML

ex.qml

import QtQuick 2.0

Item {

function myTest(){

function pgcd(a, b){
return b ? pgcd(b, a % b) : a
}

for (var i=0; i<=10000000; i=i+1){
pgcd(9078, 5118)
}

console.log("Fin du test i = ", i)
return 0
}
}

II-C-2 - Appel via Python

qmlviapython.py

import sys

from PyQt5.QtCore import QCoreApplication, QUrl, QMetaObject, Qt
from PyQt5.QtQml import QQmlComponent, QQmlEngine

def fonction():
app = QCoreApplication(sys.argv)

engine = QQmlEngine()

component = QQmlComponent(engine)
component.loadUrl(QUrl('ex.qml'))
test = component.create()

QMetaObject.invokeMethod(test, "myTest", Qt.DirectConnection)
del engine

fonction()

II-C-3 - Appel via Cython

qmlviacython.pyx

import sys

from PyQt5.QtCore import QCoreApplication, QUrl, QMetaObject, Qt
from PyQt5.QtQml import QQmlComponent, QQmlEngine

cpdef fonction():
app = QCoreApplication(sys.argv)

engine = QQmlEngine()

component = QQmlComponent(engine)
component.loadUrl(QUrl('ex.qml'))
test = component.create()

QMetaObject.invokeMethod(test, "myTest", Qt.DirectConnection)
del engine

fonction()

II-C-4 - Appel via C++

qmlviacpp.cpp

#include <QApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickView>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("ex.qml"));
QObject *object = component.create();
QMetaObject::invokeMethod(object, "myTest", Qt::DirectConnection);
return app.exec();
delete object;
}

II-D - Code C++

codecpp.cpp

#include <stdio.h>
int pgcd(int a, int b)
{
return b ? pgcd(b, a % b) : a;
}
int main(void)
{
for (int i=0; i<=10000000; i=i+1);
pgcd(9078, 5118);
printf("Fin du test i = 10000000");
return 0;
}

Voila, tous nos codes sont écrits. Pour ceux utilisant Cython ou C++ une compilation complémentaire est nécessaire. Je vous invite pour cela à lire les différentes documentations présentes sur developpez.com.

Nous allons pouvoir maintenant passer aux tests.

III - Tests des codes créés

Pour les utilisateurs linux, je vous propose de créer un bash permettant de faire nos tests. Au moment de rédiger cet article, je n'ai pas la possibilité de faire des tests sur d'autres OS mais je présume qu'il est possible de faire quelque chose de similaire à ce qui va suivre.

Voici le bash proposé :

lance.sh

echo
echo "==========================================="
echo "Temps exécution en Python :"
time python3 codepython.py
echo
echo "==========================================="
echo "Temps exécution en Cython:"
time python3 lancecython.py
echo
echo "==========================================="
echo "Temps exécution en QML appelé via Python :"
time python3 qmlviapython.py
echo
echo "==========================================="
echo "Temps exécution en QML appelé via Cython :"
time python3 lanceqml.py
echo
echo "==========================================="
echo "Temps exécution en C++ :"
time ./codecpp
echo
echo "==========================================="
echo "Temps exécution en QML appelé via C++ :"
time ./qmlviacpp

Il ne reste plus qu'à lancer lance.sh via la commande et à constater les résultats :

./lance.sh

Voici dans mon cas le résultat obtenu :

On pourra remarquer plusieurs choses :

- le code Python est vraiment long ce qui peut obliger le développeur à utiliser plusieurs threads s'il ne veut pas figer sont programme lors de certains calculs ;

- le code Cython permet un gain d'environ 50 % par rapport au code Python, ce qui peut être assez sympa puisque beaucoup de code peuvent être transcrit rapidement. Cependant ceci nécessite le passage par une compilation ;

- le code QML lancé par Python ou Cython est environ identique en termes de temps d'exécution, par contre cette manière permet de gagner beaucoup par rapport au code Python. On peut en déduire que des fonctions écrite en QML seront plus rapidement exécuter que des fonctions Python ;

- le code QML lancé par C++ est plus long que le même lancé via Python mais demeure plus rapide que du pur Python ;

- le code C++ écrase tout est reste le code le plus rapide en termes d'exécution.

IV - Conclusion

J'utilise depuis quelque temps PyQt, mais est découvert que très récemment Qt Quick. Ces simples tests m'ont permis de mettre le doigt sur un point important : il est très certainement plus intéressant de coder un maximum de chose en QML, celui-ci permettant un net gain de performance sans même devoir utiliser Cython.

V - Remerciements

Je tiens à remercier tout particulièrement …. et …. pour leur relecture attentive.