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
Sélectionnez

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
Sélectionnez

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
Sélectionnez
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
Sélectionnez
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
Sélectionnez
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
Sélectionnez
#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
Sélectionnez
#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
Sélectionnez
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 :

 
Sélectionnez
./lance.sh

Voici dans mon cas le résultat obtenu :

Image non disponible

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.