• Aucun résultat trouvé

6.2 Application au calcul du WCET d’un programme OCaLustre : l’outil Bytecrawler

6.2.4 Exemple d’application

Dans cette section, nous illustrons le fonctionnement de bytecrawler sur un court exemple. Le pro-gramme OCaLustre de cet exemple est réduit à un seul nœud nommé count qui représente un compteur : il calcule à chaque instant synchrone le successeur de l’entier calculé à l’instant précédent. De plus, ce compteur peut être remis à zéro dès que le paramètre reset du nœud est vrai.

Le code de ce nœud (à gauche), ainsi que l’ensemble des instructions bytecode vers lesquelles ce nœud est traduit suite à la compilation d’OCaLustre vers un programme OCaml puis la compilation standard de ce dernier (à droite5) sont présentés ci-après :

let%node count reset ~return:cpt =

cpt = (0 ≫if reset then 0 else (cpt + 1))

L1: GRAB 1 ACC 0 GETFIELD 0 PUSH ACC 2 BRANCHIFNOT L7 CONST 0 BRANCH L6 L7: ACC 0 OFFSETINT 1 L6: PUSH ACC 1 PUSH ACC 3 SETFIELD 1 ACC 0 PUSH ACC 3 SETFIELD 0 CONST 0 RETURN 4

Nous déclarons désormais deux fonctions OCaml responsables de la captation des entrées et du traitement des sorties de ce programme synchrone. La première définit la valeur en entrée du nœud (reset) comme le résultat du test permettant de vérifier si la broche numéro 0 du port B est alimentée, par exemple si un bouton-poussoir branché à cette broche a été pressé :

(* fonction d’entrée *) let input_count_reset () = read_bit PORTB PB0 L4: CONST0 PUSH CONST1 CCALL caml_avr_read_bit, 2 RETURN1

Puisque la valeur retournée par la lecture de cette broche (via l’appel à la primitive caml_avr_read_bit) ne peut pas être connue au moment de la compilation, elle sera représentée dans Bytecrawler sous la forme d’une valeur inconnue (>).

Par souci de simplicité, nous déclarons que la deuxième fonction censée traiter la valeur cpt calculée par l’instant synchrone ne fait « rien », c’est-à-dire qu’elle calcule simplement la valeur « unit » :

(* fonction de sortie *)

let output_count cpt = ()

L3: CONST 0 RETURN 1

Après initialisation de l’état du programme synchrone, selon le modèle de compilation décrit dans la section précédente, le programme OCaml se charge (en boucle) de lire l’entrée du programme, d’appeler la fonction de pas de l’instant synchrone, et d’appeler la fonction de traitement de ses sorties. Afin que Bytecrawler puisse reconnaître dans le bytecode où commence et où finit chaque instant synchrone, la boucle du programme est remplacée pour l’analyse de WCET par des appels aux primitives begin_loop et end_loop :

6.2. Application au calcul du WCET d’un programme OCaLustre : l’outil Bytecrawler 167

let () =

let _st = count_alloc () in

begin_loop ();

let reset = input_count_reset () in

count_step _st reset;

output_count _st.count_out_cpt; end_loop ()

Le bytecode associé au code encadré par les appels à begin_loop et end_loop est alors le suivant :

CCALL begin_loop, 1 CONST 0 PUSH ACC 5 APPLY 1 PUSH ACC 0 PUSH ACC 2 PUSH ACC 4 APPLY 2 ACC 1 GETFIELD 1 PUSH ACC 5 APPLY 1 CONST 0 CCALL end_loop, 1

Bytecrawler exécute le programme depuis la toute première instruction bytecode, mais ne commence à comptabiliser les coûts des instructions rencontrées que dès l’appel à la primitive begin_loop, et jusqu’à l’appel à end_loop, permettant ainsi de calculer le coût (en cycles) de l’instant synchrone. Ainsi, avec une table de coûts d’instructions calculée par Bound-T pour un ATmega2560, le nombre de cycles maximal estimé par Bytecrawler est de 3080. Puisqu’un tel microcontrôleur a une fréquence d’horloge de 16 MHz, alors la durée d’exécution pire cas d’un instant synchrone de ce programme est de 1, 92 × 10−4secondes

(192 µs). Ainsi, si l’intervalle entre deux changements de l’état de la broche PB0 est supérieur à 192 microsecondes, l’hypothèse synchrone est valide.

Conclusion du chapitre

Dans ce chapitre, nous avons tiré profit de la factorisation des analyses apportée par la représentation des programmes sous forme de bytecode afin de calculer le temps d’exécution pire cas d’un programme. La preuve de la correction de la méthode utilisée permet d’assurer sa viabilité, et un outil logiciel suivant cette méthode a été implémenté. Bien sûr, un des processus de certification du fonctionnement de cet outil imposerait de formaliser et prouver la méthode non plus sur le bytecode idéalisé présenté dans cette section, mais sur le vrai sous-ensemble du langage d’instructions bytecode OCaml issu de la compilation d’OCaLustre. Bien que constituant un travail conséquent en raison du nombre d’instructions du bytecode OCaml, cette transposition semble néanmoins plutôt directe.

Par ailleurs, l’intérêt principal de la méthode concerne, à nouveau, sa portabilité : les analyses portant sur le bytecode et celles inhérentes à la plateforme utilisée sont distinctes [HK07]. Le développeur d’applications n’a ainsi pas besoin de faire usage d’annotations particulières pour réaliser le calcul du WCET d’un programme OCaLustre. Ces annotations (ayant par exemple trait au nombre de tours d’une boucle) peuvent toutefois être utilisées par le développeur de la plateforme, afin de fournir une table de coûts des instructions et des primitives. Une fois créée, cette table suffit alors à l’analyse du bytecode de tout programme OCaLustre. La portabilité de la méthode a pour conséquence la portabilité de l’outil Bytecrawler : pour un même programme, le simple fait de fournir une table de coûts mesurée pour un autre microcontrôleur permet de déduire le WCET du programme sur cette plateforme, sans même avoir à le recompiler.

La méthode présentée dans ce chapitre pourrait certainement bénéficier d’analyses statiques supplé-mentaires, grâce par exemple à l’exécution symbolique [BFL18], ou concolique [Sen07] (une approche hybride mêlant, d’une façon plutôt proche de notre méthode, des valeurs concrètes et des valeurs sym-boliques), qui permettrait de ne pas considérer les chemins d’exécution inatteignables. Toutefois, cette méthode constitue avant tout une illustration d’une analyse tirant profit d’une représentation commune (le bytecode), plutôt qu’une technique d’estimation la plus fine possible du temps d’exécution maximal d’un programme.

Enfin, il est intéressant de remarquer que la méthode présentée dans ce chapitre semble pouvoir être facilement étendue pour la réalisation d’autres mesures que celle de temps d’exécution : en changeant seulement la définition de la fonction de coût (et donc la table qui la représente), il serait possible d’évaluer d’autres critères, et d’estimer par exemple la taille maximale de la pile d’exécution d’un programme synchrone, ou même la quantité maximale de valeurs allouées dans le tas pendant l’exécution d’un programme. Ces considérations relatives à l’empreinte mémoire des programmes seront déterminantes dans le chapitre suivant, au sein duquel nous détaillerons différentes mesures permettant de juger des performances des principales solutions présentées dans cette thèse.

169

7 Performances d’OMicroB et OCaLustre

Ce chapitre est destiné à la présentation et l’analyse de plusieurs mesures qui visent à constater la puissance des solutions logicielles décrites dans ce document. Nous y présentons en premier lieu un ensemble de mesures réalisées sur la machine virtuelle OMicroB à partir de programmes destinés à tester les capacités de cette dernière à l’aune des divers aspects du langage OCaml. Puis, nous traiterons des performances d’OCaLustre en particulier via l’analyse de la consommation en ressources mémoire d’un programme OCaLustre complet, que nous comparerons avec un programme équivalent réalisé dans le langage Lucid Synchrone. Tout au long du chapitre, nous discuterons en détail des résultats de ces mesures, et nous nous attacherons à les mettre en perspective relativement aux performances de solutions préexistantes.

7.1 Mesures de performances d’OMicroB

Dans cette section, nous réalisons plusieurs mesures permettant d’évaluer les performances de la machine virtuelle OMicroB. Ces mesures, réalisées à partir de programmes de test simples, concernent la vitesse et la consommation mémoire de la machine virtuelle.