TangenteX.com

Le besoin

Dans beaucoup de codes de calcul, il est utile de connaitre le temps d'exécution d'une portion du code. Cela peut servir à comparer le temps de calcul du même code sur plusieurs machines différentes. On peut aussi vouloir comparer la rapidité de calcul selon la méthode numérique utilisée. Ou encore mesurer l'influence d'un paramètre sur le temps de calcul, par exemple l'influence de la valeur d'un critère de convergence sur le temps de calcul.

La méthode est bien connue des numériciens, mais peut-être pas des débutants, aussi ai-je développé un petit module utilitaire, chrono.py, qui renferme deux routines qui rendent ce service.

Le module chrono

Les routines

Pour mesurer le temps d'exécution d'une portion de code, il faut démarrer un chronomètre juste avant de rentrer dans la portion de code à mesurer et arrêter le chronomètre juste en sortant de cette portion de code. Puis d'afficher le temps de traitement.

La précision d'affichage du temps sera de la dizaine de ms, suffisante pour nos besoins et compatible avec la précision de l'horloge de mon ordinateur. La résolution du temps dépend de l'ordinateur et du système d'exploitation. Pour donner un ordre d'idée, sur un PC sous Windows, la résolution est de 1 ms (on peut descendre en dessous) et sur un Mac sous OSX elle est meilleure qu'une milliseconde (voir ici pour plus de détails).

Car bien sûr, je vais utiliser l'horloge de mon ordinateur, à laquelle j'accède par les routines standards de l'OS (OSX, Windows ou Linux). Et comme il peut être peu pratique d'accéder directement à l'OS, je vais utiliser les fonctions fournies par les différents langages de programmation. Tous ceux que j'utilise dans TangenteX possèdent ces fonctions. J'ai choisi ici Python, mais cet outil peut être écrit en C, C++, FORTRAN, MatLab et autres.

Pour information, dans tous les langages cités ci-dessus, les fonctions de gestion du temps font appel aux fonctions C de gestion du temps, les OS et les langages étant presque tous écrit en C (sauf java et encore...).

La routine DebutChrono

Le code de la routine DebutChrono est assez trivial :

def DebutChrono():
    """ Lancement du chronomètre en début de traitement """
    global TDebut
    TDebut = time.time()
    return

Je déclare une variable globale TDebut, c'est à dire accessible depuis tous les endroits d'un script python, pour contenir la date et l'heure système au déclenchement du chronomètre. Cette heure est récupérée depuis l'horloge système par l'appel à la fonction système time().

La routine StopChrono

Le code de la routine StopChrono est un peu plus long :

def StopChrono(message) :
    """
    Arrêt du chronomètre et affichage du temps écoulé en fin de traitement
    La variable  message peut contenir le nom du code de calcul ou n'importe
    quel autre message.
    """
    global TDebut
    TFin = time.time()
    duree = TFin - TDebut
    print('%s  - Temps de traitement : %.2f secondes' % (message,duree))
    TDebut = 0
    message = ''
    return

Après avoir récupéré le temps courant TFin de l'horloge système à la sortie du code à instrumenter, je soustrais l'heure de début contenue dans la variable globale TDebut à TFin et j'obtiens ainsi le temps passé entre le début et la fin du traitement.

Attention toutefois : il s'agit bien du temps "ordinateur" qui s'est écoulé entre le début du traitement de la portion de code instrumenté et la fin de ce traitement. Mais rien ne prouve que durant cet intervalle de temps, l'ordinateur n'a fait que traiter notre code de calcul. Tous les OS sont multitâches et donc l'OS a exécuté pendant cet intervalle d'autres tâches. Le temps affiché n'est donc qu'une indication. Sa justesse dépend de l'activité de l'ordinateur. Elle est satisfaisante sur un PC ou un Mac sur lequel ne fonctionnent pas d'autres applications en tâche fond. Par contre, si vous l'utilisez sur un mini ou un grand calculateur partagé ou sur un PC qui fait autre chose, vous risquez des surprises !

Après avoir calculé le temps écoulé pendant le traitement de mon code, je l'affiche en y ajoutant un message personnalisé, qui peut par exemple être le nom du traitement instrumenté.

Construire un module python

Construire un module qui sera réutilisable dans tous vos scripts python est très simple. Il suffit de construire un script contenant toutes les fonctions à partager et de déposer ce script dans votre répertoire de travail, celui dans lequel vous exécutez vos scripts, ou dans un répertoire dédié à vos modules sous réserve de déclarer son chemin d'accès dans la variable d'environnement §PYTHONPATH.

Voici le code de mon script chrono.py :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
 Module de mesure du temps de traitement d'un code
"""
__author__      = "Dominique Lefebvre"
__copyright__   = "Copyright 2020 - TangenteX.com"
__version__     = "1.0"
__date__        = "6 avril 2020"
__email__       = "dominique.lefebvre@tangentex.com"

import time

# fonction de déclenchement du chronomètre
def DebutChrono():
    """ Lancement du chronomètre en début de traitement """
    global TDebut
    TDebut = time.time()
    return
   
# fonction de stop du chronomètre
def StopChrono(message) :
    """
    Arrêt du chronomètre et affichage du temps écoulé en fin de traitement
    La variable  message peut contenir le nom du code de calcul ou n'importe
    quel autre message.
    """
    global TDebut
    TFin = time.time()
    duree = TFin - TDebut
    print('%s  - Temps de traitement : %.2f secondes' % (message,duree))
    TDebut = 0
    message = ''
    return

Le script du module chrono.py est téléchargeable dans la bibliothèque de codes de TangenteX.

Un exemple d'utilisation

Pour illustrer l'usage du module chrono, j'ai repris un de mes codes de calcul, celui de la résolution numérique de l'équation de Laplace par la méthode de Gauss-Seidel dans le script LaplaceGaussSeidel.py. Il est téléchargeable dans la bibliothèque de codes de TangenteX.

J'ai modifié son code source pour intégrer mes routines de chrono :

# importation des librairies
import numpy as np
import matplotlib.pyplot as plt
from chrono import DebutChrono, StopChrono

pour l'importation du module chrono et :

DebutChrono()
while ecart > EPS:   
    # sauvegarde de la grille courante pour calculer l'écart
    Vprec = V.copy()
    # calcul de la nouvelle valeur du potentiel sur la grille
    V[1:-1,1:-1]= 0.25*(Vprec[0:-2,1:-1] +V[2:,1:-1] + Vprec[1:-1,0:-2] + V[1:-1,2:])
    # critère de convergence
    ecart = np.max(np.abs(V-Vprec))
    iteration += 1
# fin du calcul
StopChrono(u"Méthode de Gauss-Seidel")

pour l'intrumentation du code dont je veux mesurer le temps d'exécution.

Comme vous le constatez, la mise en oeuvre de chrono n'est pas très compliquée.

Contenu et design par Dominique Lefebvre - www.tangenteX.com avril 2020    Licence Creative Commons    Contact :