Prudence : document en construction, et structure en émergence...
Pour un résumé des symboles typiques pour les éléments clés des différents tests « d'ordonnançabilité » que vous trouverez dans la littérature, vous pouvez vous référer à ceci (version PDF) ou à cette version HTML.
Ce qui suit implémente concrètement certains des tests « d'ordonnançabilité » vus en classe. Notez que le terme « ordonnançabilité » n'est pas français, mais que je l'utiliserai à titre de néologisme pour le terme anglais Schedulability, que l'on retrouve abondamment dans la littérature des STR.
Nous donnerons à « ordonnançable » le sens de « qui peut être ordonnancé », et nous donnerons à « ordonnançabilité » le sens de « caractère de ce qui est ordonnançable ». Notez que j'omettrai les guillemets pour écrire ordonnançabilité tout simplement à partir de ce point, dans le but d'alléger la présentation.
Un ordonnanceur TR est souvent appelé à déterminer s'il est possible d'ordonnancer un ensemble de tâches TR, tout comme il est appelé à réaliser cet ordonnancement. Alors que la mise en place d'un ordonnancement complet peut être coûteux, il est souvent possible de tester l'ordonnançabilité d'un ensemble de tâches à coût plus raisonnable.
Pour cette raison, nous examinerons ici la question des tests d'ordonnançabilité.
Un test d'ordonnançabilité nous permettra de statuer si un ensemble de tâches peut être ordonnancé ou encore s'il ne le peut pas. Dans le contexte d'un STR, est ordonnançable un ensemble de tâches pour lequel il existe un ordonnancement tel que les moments de lancement de chacune sont respectées (ce qui équivaut au respect des périodes quand les tâches sont périodiques) et pour lequel chaque tâche pourra s'exécuter dans le respect de ses contraintes.
Un test d'ordonnançabilité peut être... |
|
---|---|
Suffisant |
Un test est suffisant si, lorsqu'il indique qu'un ensemble de tâches est ordonnançable, alors il l'est. Cependant, si un tel test conclut par la négative, cela ne signifie pas que l'ensemble n'est pas ordonnançable, seulement que nous ne pouvons conclure de manière affirmative à partir dudit test. Essentiellement, un test suffisant ne donne pas de faux positifs |
Nécessaire |
Un test est nécessaire si, lorsqu'il est échoué, nous savons que l'ensemble de tâces auquel il a été appliqué n'est pas ordonnançable. S'il est réussi, toutefois, la capacité d'ordonnancer l'ensemble de tâches n'est pas garantie |
Exact |
Un test est exact s'il est à la fois nécessaire et suffisant. En pratique, réaliser un test d'ordonnançabilité qui soit exact est souvent prohibitif (Intractable, au sens anglais du terme) sur le plan des coûts de calcul |
Durable |
Dans un STR, par la force des choses, on trouve typiquement des tests d'ordonnançabilité quelque peu pessimistes. Sachant cela, un test est durable (Sustainable) s'il continue à bien prédire l'ordonnançabilité d'un système de tâches quand les conditions d'exécution de ce dernier s'améliorent |
Pour l'ensemble de ce qui suit, nous aurons besoin d'être en mesure de représenter une tâche. étant donné les démonstrations faites plus bas, je me suis limité à une représentation qui convienne à des tâches périodiques, et il est probable que des ajustements soient requis pour couvrir aussi les tâches apériodiques ou sporadiques.
Dans une optique d'économie de code et dans le respect du principe DRY, nous utiliserons une implémentation CRTP des opérateurs d'ordonnancement. L'idée ici est de pouvoir classer des objet en ordre croissant ou décroissant, les trier ou les organiser de diverses manières tout en évitant une part de redondance dans le code. Le mot « ordonnancement » tel qu'utilisé ici n'a pas de lien direct avec la question de l'ordonnançabilité et des tests sur lesquels nous nous penchons dans ce document. |
|
Pour les besoins de la démonstration, nous aurons recours à une représentation des tâches à ordonnancer qui est telle que chaque tâche connaît son WCET, la période à laquelle elle doit être démarrée (rythme constant) et sa priorité. Cette représentation ne convient donc pas à un algorithme d'ordonnancement TR qui dépendrait de l'échéance (du Deadline) des tâches plutôt que de la priorité et de la période – on peut penser ici à l'algorithme EDF par exemple. Pour réaliser un ordonnancement respectant le principe de Rate Monotonicity dans un système à base de priorités, il importe que l'ordre de priorité des tâches d'un ensemble soit l'inverse de celui de leurs périodes (plus courte période, plus haute priorité). La fonction rate_monotonic_priorities() que vous voyez à droite prend une séquence de ce qu'on présume être des Tache<T> pour un certain type T et s'assure que cette contrainte soit respectée. Ceci permet de valider, pour certains des tests décrits plus bas, que le test en question soit applicable pour un ensemble de tâches donné. L'opérateur de projection sur un flux n'a qu'une utilité diagnostique. |
|
Examinons maintenant l'implémentation de quelques tests d'ordonnançabilité tirés directement de la littérature portant sur les STR et sur les ordonnanceurs inéquitables. Nous séparerons l'implémentation de chaque test de son code client (du code de test, dans ce cas-ci. Pour l'ensemble des tests ci-dessous, supposez que les inclusions et les directives using à droite ont été faites. Ceci permettra d'alléger l'écriture. J'ai choisi d'y aller avec du code C++ 11, aussi dans un souci d'allègement de l'écriture. Quelques a prioriPour les tests d'ordonnançabilité couverts ici, par souci de simplicité, nous présumerons :
L'indépendance des tâches implique un lancement (Release) à un moment précis de toutes les tâches. Ce moment est l'instant critique du système. |
|
Liu et Layland (1973)Ce test opère sur des ensembles de tâches à priorités fixées a priori. Le seuil évalué par seuil() converge vers pour de grandes valeurs de . Ainsi, toute liste de tâches dont le taux d'utilisation est inférieur ce seuil sera ordonnançable avec une telle approche. Le test va comme suit (voir ceci pour un rappel du sens des symboles utilisés) : \[ \sum_{i=1}^N{\left(\frac{C_i}{T_i}\right)} \leq N\left(2^\frac{1}{N}-1\right)\]
Il est suffisant mais pas nécessaire; par exemple, pour l'ensemble de tâches suivant :
...on obtient un seuil de valeur et un test , or bien que le test soit faux, cet ensemble s'avère ordonnançable. Vous verrez ici comment ce test est mis en application. |
|
Bini (2007)Tout comme Lui et Layland (1973), ce test est suffisant sans être nécessaire, et opère sur des ensembles de tâches à priorités fixes. Il est toutefois un peu plus simple à calculer. Le test va comme suit (voir ceci pour un rappel du sens des symboles utilisés) : \[ \prod_{i=1}^N{\left(\left(\frac{C_i}{T_i}\right)+1\right)} \leq 2 \]
Vous verrez ici comment ce test est mis en application. |
|
Raffinements possiblesCertains raffinements sont possibles pour réaliser des tests d'ordonnançabilité. L'un d'entre eux repose sur la capacité de grouper des tâches en familles. Les tâches d'une même famille ont alors en commun d'avoir des périodes qui sont des multiples l'une de l'autre; les considérer en groupe permet de réduire le nombre de cas distincts à examiner. L'algorithme à droite est mais est typiquement fait offline, et a donc peu d'impact sur le temps d'exécution en pratique. |
|
Les tests eux-mêmes seront réalisés en passant un test d'ordonnançabilité générique à la fonction realiser_test() visible à droite. Ceci permettra de réduire la répétition de code. |
|
Appliquer le test Liu et Layland (1973)Trois exemples d'application du test Liu et Layland (1973) sont présentés à droite :
|
|
Appliquer le test Bini (2007)Trois exemples d'application du test Bini (2007) sont présentés à droite :
|
|
Vous trouverez ici le détail de certains composants clés des tests d'ordonnançabilité.
La fonction evaluer_interference_maximale() évalue l'interférence maximale que subira une tâche dans un intervalle de temps donné. étant donné que les calculs débutent typiquement à l'instant critique pour un ordonnanceur donné, et que cet instant est typiquement considéré comme l'instant 0 pour les fins des calculs, le paramètre intervalle dans la fonction est dans .
L'ensemble contient les tâches de priorité supérieure à celle de ; le symbole signifie d'ailleurs ici Higher Priority Than. évidemment, la constitution de dépend directement de l'ensemble de tâches à ordonnancer, de même que de la priorité de la tâche elle-même.. La construction de cet ensemble est une tâche somme toute banale. On pourrait faire plus élégant en utilisant un algorithme comme std::partition(), si vous avez envie de vous amuser un peu. |
|
Le calcul du temps de réponse d'une tâche peut tenir compte des interférences (pour les tâches autres que la plus prioritaire d'entre toutes) ou ne pas en tenir compte (pour la tâche la plus prioritaire du lot). L'équation décrivant ce calcul est donnée par : \[ R_i = C_i + \sum_{j \in hp(i)} \left\lceil \frac{R_i}{T_j}
\right\rceil C_j \]
Cette équation est plus facile à résoudre numériquement en commençant par la tâche la plus prioritaire (qui ne subit pas d'interférences d'autres tâches) et en « reculant » vers les tâches les moins prioritaires. |
|
Pour déterminer un intervalle réaliste pour chaque tâche , nous avons recours à une évaluation débutant par la dernière tâche (la plus prioritaire) et procédant à rebours, cumulant les résultats en cours de route. |
|
Enfin, le programme principal applique ces quelques tests à des ensembles de tâches donnés. |
|
À l'exécution, nous obtenons ce qui suit.
Liu et Layland, 1973
Ce test s'applique dans le cas ou les taches sont periodiques, ne partagent pas de ressources,
et ou l'echance de chaque tache est egale a sa periode. Les priorites doivent etre statiques
et les priorites doivent etre choisies de maniere a ce que les taches de plus courte periode
soient les plus prioritaires. Le temps d'un changement de contexte est presume negligeable
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Taches a ordonnancer: {p:1, P:50, C:12} {p:2, P:40, C:10} {p:3, P:30, C:10}
Resultat attendu: echec (legitime)
Test echoue, score 0.823333, seuil 0.779763
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Taches a ordonnancer: {p:1, P:80, C:40} {p:2, P:40, C:10} {p:3, P:20, C:5}
Resultat attendu: echec (faux negatif)
Test echoue, score 1, seuil 0.779763
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Taches a ordonnancer: {p:1, P:80, C:40} {p:2, P:40, C:10} {p:3, P:20, C:5}
Apres groupement en familles: {p:1, P:80, C:80}
Resultat attendu: succes (legitime)
Test reussi, score 1, seuil 1
Bini, 2007
Ce test s'applique dans le cas ou les taches sont periodiques, ne partagent pas de ressources,
et ou l'echance de chaque tache est egale a sa periode. Les priorites doivent etre statiques
et les priorites doivent etre choisies de maniere a ce que les taches de plus courte periode
soient les plus prioritaires. Le temps d'un changement de contexte est presume negligeable
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Taches a ordonnancer: {p:1, P:50, C:12} {p:2, P:40, C:10} {p:3, P:30, C:10}
Resultat attendu: echec (legitime)
Test echoue, score 2.06667, seuil 2
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Taches a ordonnancer: {p:1, P:80, C:40} {p:2, P:40, C:10} {p:3, P:20, C:5}
Resultat attendu: echec (faux negatif)
Test echoue, score 2.34375, seuil 2
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Taches a ordonnancer: {p:1, P:80, C:40} {p:2, P:40, C:10} {p:3, P:20, C:5}
Apres groupement en familles: {p:1, P:80, C:80}
Resultat attendu: succes (legitime)
Test reussi, score 2, seuil 2
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Evaluer temps de reponse, v0 (calcul de base)
Tache : {p:1, P:50, C:12}; WCET: 12
Tache : {p:2, P:40, C:10}; WCET: 10
Tache : {p:3, P:30, C:10}; WCET: 10
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Evaluer temps de reponse, v1 (inclure interference), [0..Ri) avec Ri == 50
Tache : {p:1, P:50, C:12}; WCET: 20
Tache : {p:2, P:40, C:10}; WCET: 20
Tache : {p:3, P:30, C:10}; WCET: 10
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Evaluer temps de reponse, v2 (avec interference), [0..Ri) avec Ri == 50
Tache : {p:1, P:50, C:12}; WCET: 20
Tache : {p:2, P:40, C:10}; WCET: 20
Tache : {p:3, P:30, C:10}; WCET: 10
Un test d'ordonnançabilité pour EDF remonte aussi à Liu et Layland (1973). Ce test suppose que , donc que l'échéance d'une tâche est égale à sa période, et va comme suit (voir ceci pour un rappel du sens des symboles utilisés) : \[ \sum_{i=1}^N{\left(\frac{C_i}{T_i}\right)} \leq
1\]
La simplicité de ce test est appréciable : ici, dans la mesure où le taux d'utilisation est inférieur à , le critère d'ordonnançabilité est rencontré. Le test d'ordonnançabilité ci-dessus est exact pour EDF alors que l'équivalent est seulement suffisant pour FPS. On sait par contre que tout ordonnancement valide avec un algorithme FPS serait aussi valide avec EDF. |
|
Il est possible, avec EDF, d'évaluer la charge du système à un instant par (voir ceci pour un rappel du sens des symboles utilisés) : \[ h(t)=\sum_{i=1}^N{\lfloor \frac{t+T_i-D_i}{T_i}\rfloor
C_i} \]
Pour EDF, en pratique, on mesurera la charge du système aux moments correspondant aux échéances des tâches, et non pas de manière continue. Notez que cet exemple ne profite pas des avancées de C++ 11 avec <chrono>. Il existe aussi une limite analytique à partir de laquelle il n'est plus pertinent d'évaluer la charge d'un système ordonnancé à partir de cette stratégie (à tout le moins s'il n'y a pas de nouvelles tâches qui s'ajoutent à l'ensemble devant être ordonnancé), que je n'ai pas le temps de détailler pour le moment (désolé!). |
|
Voilà!