Limiter les surcharges via l`ordonnancement du seuil de préemption
Transcription
Limiter les surcharges via l`ordonnancement du seuil de préemption
Application OS temps réel Limiter les surcharges via l’ordonnancement du seuil de préemption Dans un système embarqué temps réel les threads, ou tâches applicatives, exécutent leur travail grâce à un système d'exploitation temps réel qui fait appel à un ordonnancement préemptif afin de s'assurer que les threads critiques sont bien prioritaires. Mais, dans certains cas, l'ordonnancement préemptif engendre des surcharges (overhead) liées aux commutations de contexte. La technologie de l’ordonnancement du seuil de préemption, peut néanmoins être utilisée pour réduire les overhead. Auteur John A. Carbone, Directeur Marketing Express Logic A vant d’aborder l’ordonnancement du seuil de préemption, il est important de bien appréhender les notions techniques de base des systèmes d'exploitation temps réel. Le « thread » est le premier terme à expliciter, sachant que certains OS temps réels utilisent parfois le terme « tâche ». Dans la plupart des cas, ces termes sont synonymes. Un thread est une sorte de fonction au sein d’un programme principal, qui s’exécute jusqu'à son terme ou jusqu'à ce qu'il soit interrompu. Parfois, les threads peuvent se placer en pause dans l’attente d’un évènement particulier. Les threads partagent un espace d'adressage et s'exécutent de façon pseudo-parallèle sur le même CPU. Dans les systèmes multicœurs, les threads peuvent réellement s'exécuter de manière parallèle, un sur chaque cœur, et partager par la suite ce cœur avec d'autres threads. Les threads sont utilisés afin de structurer un programme en parties modulaires, pour une gestion plus simple de l’application et pour permettre à plusieurs membres d'une équipe de participer au développement. Dans la pratique, les threads peuvent se trouver dans les états suivants : - PRET : le thread est prêt à s'exécuter, n’est pas en attente d'un événement, ne nécessite aucune intervention extérieure, mais il n'est pas en train d'exécuter des instructions ; - EN COURS :le thread exécute des instructions ; - EN PAUSE : le thread n'est pas prêt à s'exécuter, parce qu'il est en attente 36 / L’EMBARQUÉ / N°1 d’un message dans la file d’attente, d’un sémaphore, d’un compteur qui doit se terminer, etc. ; - TERMINE : le thread a terminé le traitement qu’il devait effectuer et n'a plus vocation à s'exécuter A chaque thread est attribuée une priorité qui indique son importance relative et l’ordre dans lequel il aurait accès au CPU au cas où tous les threads seraient prêts à s'exécuter simultanément. En général, les priorités sont représentées par un nombre entier de 0 à N, avec le 0 soit en haut, soit en bas de l'échelle des priorités. Pour le système d'exploitation temps réel ThreadX, nous affectons le 0 au thread de plus haute priorité, et les chiffres plus élevés correspondent à des priorités moins importantes. A chaque thread est donc attribuée une priorité, et cette priorité peut être modifiée de façon dynamique. Plusieurs threads peuvent se voir affecter la même priorité, ou tous peuvent avoir une priorité unique. Lorsqu’un thread est en cours d'exécution et qu'un autre thread ayant une priorité plus élevée est dans l'état « PRET » à s'exécuter, le système d'exploitation temps réel interrompt ou préempte le thread en cours et le remplace par le thread ayant une priorité plus importante. On appelle ce processus « la commutation de contexte » (tableau I). Lors d’une commutation de contexte, un So temps réel sauvegarde le contexte du thread en cours sur sa pile, récupère le contexte du nouveau thread depuis sa pile, et le stocke dans les registres et le compteur ordinal du processeur. Ainsi il y a eu commutation de contexte entre les deux threads. Le processus de commutation de contexte s'avère assez complexe et peut nécessiter de 50 à 500 cycles d’horloge, selon le système d'exploitation temps réel et le processeur. Il faut donc prendre soin d'optimiser les opérations de commutation de contexte dans un RTOS, et de minimiser le recours à de telles opérations. C’est l’objectif de l’ordonnancement du seuil de préemption. Des ordonnanceurs préemptifs Les applications qui n’utilisent pas de système d'exploitation temps réel mais qui intègrent plusieurs opérations ou fonctions – des tâches ou des threads la plupart du temps – doivent inclure un mécanisme pour lancer la fonction qui doit s'exécuter. Une simple boucle séquentielle, voire des boucles plus sophistiquées, peuvent être utilisées afin de vérifier l'état d’une fonction donnée, la lancer si elle a un traitement à effectuer et l’ignorer dans le cas contraire. Ces boucles ressemblent à des ordonnanceurs, mais elles ont tendance à manquer d'efficacité et de réactivité, surtout quand le nombre de fonctions ou de threads augmente. Par contre, un ordonnanceur de systèmes d'exploitation temps réel peut surveiller l’ensemble et, à tout moment, décider rapidement quelle activité doit s’exécuter. En général, les ordonnanceurs temps L’intégration anaLogique ? Pas nécessairement requis Par tous ! © 2013 Maxim Integrated Products, Inc. All rights reserved. Maxim Integrated and the Maxim Integrated logo are trademarks of Maxim Integrated Products, Inc., in the United States and other jurisdictions throughout the world. Application OS temps réel I.- La commutation de contexte Etape Opération Cycles 1 Sauvegarder le contexte du thread en cours (les valeurs des registres GP et FP, et PC) sur la pile 20-100 2 Sauvegarder le pointeur de la pile en cours dans le blocde contrôle du thread 2-20 3 Commuter au pointeur de la pile du système 2-20 4 Retour à l'ordonnanceur 2-20 5 Trouver le thread ayant la priorité la plus haute qui est prêt à s'exécuter 2-50 6 Commuter à la pile du nouveau thread 2-50 7 Récupérer l'ancien PC du nouveau thread 20-100 8 Restaurer l'ancien PC du nouveau thread 2-40 9 Effectuer d'autres processus 0-100 Total 50-500 Une commutation de contexte est essentielle à la préemption, mais implique de nombreuses étapes et des traitements importants, comme on le voit dans cet exemple d'un OS temps réel typique. réel sont préemptifs c'est-à-dire qu'ils s’assurent que le thread de plus haute priorité prêt à s'exécuter est bien celui dont ils autorisent le lancement, et que les autres patientent. Les ordonnanceurs d'OS temps réel peuvent aussi utiliser la méthode du tourniquet (round-robin) qui est assez similaire à celle de la boucle séquentielle, ou faire appel à une méthode de tourniquet plus sophistiquée où chaque thread, plutôt que de s'exécuter jusqu'à son terme ou jusqu'à une pause volontaire, se voit allouer un certain pourcentage de temps CPU. L’ordonnanceur de l'OS temps réel exécute des commutations de contexte quand il le faut et place les threads en sommeil, les oblige à renoncer à leur temps CPU, les force à se terminer et à disparaitre du groupe de threads en attente de l'accès au CPU. Le terme « multithreading » (ou multitâche) indique que le CPU est partagé par plusieurs threads. Dans cette configuration, si un thread rencontre un blocage, plutôt que de vérifier les conditions qui lui permettraient de continuer, il cède l'accès au CPU aux autres threads en attente prêts à être exécutés. Ainsi cette méthode 1 Préemption d'un thread Assigner un seuil de préemption plus élevé que la priorité du thread évite sa préemption par des threads dont les priorités sont situées entre ces deux valeurs. 38 / L’EMBARQUÉ / N°1 assure une utilisation plus efficace des cycles CPU qui, autrement, seraient gaspillés. A titre d’exemple, dans une configuration simple à deux threads, thread_a et thread_b, supposons que thread_a est en cours d'exécution et lance une opération d'E/S pouvant nécessiter des centaines de cycles pour s'achever. Au lieu d'être placé en position d'attente dans une boucle de scrutation active, thread_a peut être mis en pause jusqu'à ce que l’opération d'E/S soit achevée et thread_b peut utiliser le CPU entretemps. Ce processus nécessite une commutation de contexte, comme décrit ci-dessus. Une fois l’opération d'E/S terminée, thread_a peut reprendre son travail. Par rapport au principe des boucles séquentielles et aux autres approches d’ordonnancement non-préemptives, le multitâche garantit une utilisation plus efficace des ressources CPU. La préemption consiste à arrêter l'exécution d'un thread afin qu’une autre action puisse s'exécuter. Ce processus peut être dû à une interruption ou à une action du thread en cours. Dans l’ordonnancement préemptif, le système d'exploitation temps réel exécute toujours le thread à plus haute priorité qui est dans l'état « PRET » à s'exécuter. En général, le contexte du thread en cours d'exécution est sauvegardé, le contexte d’un autre thread est stocké à sa place, et le nouveau thread est lancé. Les systèmes temps réel et les OS temps réels en général utilisent l’ordonnancement préemptif, car il est le plus réactif aux évènements externes, que ce soit lorsqu’un thread doit s'exécuter dès l'occurrence d'un tel évènement, ou qu’il doit s'exécuter avant une échéance particulière. Bien que la réactivité soit optimisée, la surcharge CPU est élevée puisqu'il est nécessaire de lancer une commutation du contexte. Il existe cependant certains problèmes liés à la préemption que le développeur doit éviter ou gérer de façon appropriée. Le premier est le risque qu'un thread soit placé en situation de « famine », c'est-à-dire qu'il n’arrive jamais à s'exécuter car un thread doté d'une priorité plus élevée ne se termine jamais. Les développeurs doivent éviter les situations où un thread de priorité élevée se retrouve dans une boucle infinie OS temps réel Application ou consomme trop de temps CPU, empêchant les autres threads d'accéder au processeur. Par ailleurs, en présence d'un très grand nombre de commutations de contexte, la surcharge CPU peut s'avérer pénalisante. Dans l’exemple ci-après, nous allons étudier ce type de situation avec des outils qui vont nous permettre d’observer et de mesurer cette surcharge. Autre problème éventuel : l’inversion de priorité. L'inversion de priorité peut se produire lorsqu'un thread à haute priorité est en attente d'une ressource partagée, alors que celle-ci est utilisée par un thread de faible priorité qui ne peut pas en finaliser l’usage, et ce en raison de la préemption d'un thread doté d'une priorité intermédiaire. 2 Ordonnancement préemptif vs ordonnancement du seuil de préemption Le Cas-1, qui s'appuie sur un ordonnancement totalement préemptif, affiche quatre fois plus de commutations de contexte que le Cas-2 qui utilise les mêmes threads, mais qui fait appel à l'ordonnancement du seuil de préemption. L'ordonnancement du seuil de préemption Nous allons maintenant présenter le concept à la base de l’ordonnancement du seuil de préemption. Avec cette méthode, on établit un niveau de priorité qui doit être dépassé avant qu’un thread puisse être préempté. L'ordonnancement du seuil de préemption empêche certaines préemptions et ainsi élimine plusieurs commutations de contexte, réduisant d’autant la surcharge. Normalement, tout thread affecté d'une priorité plus haute que celui qui est en cours d'exécution peut le préempter. Mais, avec l'ordonnancement du seuil de préemption, un thread en cours ne peut être préempté que si le thread est doté d'une priorité plus élevée que le 3 Comptage des interruptions La prise en compte d'un cycle complet permet de dénombrer le nombre total d'impulsions d'horloge, et de mesurer le temps réel qu'il a fallu pour exécuter l'ensemble du cycle. Cas-1 avec 1 801 impulsions d’horloge par cycle Cas-2 avec 964 impulsions d’horloge par cycle L’EMBARQUÉ / N°1 / 39 Application OS temps réel seuil de préemption du thread en cours. Dans un système purement préemptif, le seuil de préemption serait égal à la priorité du thread. En réglant un seuil de préemption à un niveau plus élevé que la priorité du thread, les threads dont les priorités sont situées entre ces deux valeurs ne pourront pas préempter. Dans l’exemple suivant (voir figure 1 en page XX), un thread affecté d'une priorité (faible) de 20 pourrait normalement être préempté par un thread ayant une priorité de 19, 18, 17, 16, etc. Mais, si le seuil de préemption est fixé à 15, alors seuls les threads dotés d'une priorité supérieure à 15 (dont la valeur réelle est inférieure à 15 donc), pourraient préempter le thread. Partant, les threads intermédiaires – ceux affectés d'une priorité de 19, 18, 17, 16 et 15 – ne peuvent pas préempter, alors que les threads dotés d'une priorité de 14 ou plus (soit 14, 13, 12 et moins) le peuvent. Le seuil de préemption est optionnel et peut être spécifié pour un thread, pour tous les threads, ou pour aucun d’entre eux. S'il n'est pas spécifié, le seuil de préemption d'un thread est égal à son niveau de priorité. Mais avec un seuil de préemption, un thread peut empêcher sa préemption par des threads affectés d'une priorité plus élevée, ceci jusqu'à une certaine limite. Au-delà de cette limite, la préemption sera autorisée. Afin d’illustrer les bénéfices en termes de performances que procure un ordonnancement du seuil de préemption, nous allons comparer une approche intégralement préemptive avec une approche avec ordonnancement du seuil de préemption et nous évaluerons les implications de ces deux approches en termes de commutation du contexte et de débit. Pour mener à bien cette comparaison, nous utilisons une simple application de type producteur/ consommateur, avec un thread qui envoie les messages et 3 threads qui les récupèrent dans des files d’attente. Nous allons enregistrer tous les évènements pour bien visualiser les actions effectuées. Ensuite, nous pourrons observer les évènements transcrits, décompter le nombre de commutations de contexte, mesurer les performances et en tirer nos conclusions. Afin de comparer les deux approches, nous allons envisager 40 / L’EMBARQUÉ / N°1 deux cas (figures 2 et 3). Le Cas-1 utilise l’approche totalement préemptive, avec attribution des valeurs de priorité 1, 2, 3 et 4 au thread A, B, C et D respectivement. Dans le Cas-2, nous ajoutons le seuil de préemption pour voir comment il peut être utilisé pour réduire le nombre de commutations de contexte. Pour ceci, nous attribuons au thread_D un seuil de préemption de 1, c'est-à-dire qu'il ne peut être préempté que par un thread ayant une priorité plus élevée que 1. Dans cette configuration, aucun thread n'a une priorité plus haute que 1 (soit 0), donc Thread_d ne sera préempté ni par A, ni par B, ni par C. neuf messages à envoyer. Le cycle est alors terminé. Au cours du cycle, 9 messages ont été envoyés, 9 ont été récupérés, et 18 commutations de contexte ont été enregistrées. Dans le Cas-2, le Thread_D ne sera pas interrompu pendant qu’il envoie ses messages. Ce Thread_D continue d'envoyer ses messages jusqu'à ce qu’une file d’attente soit pleine. A ce moment là, il se met en pause jusqu'à ce que l’une des files d’attente ne soit plus pleine. A noter qu’une fois que le Thread_D se met en pause, il ne pourra reprendre la main que lorsque les Threads A, B et C seront bloqués puisque ce Thread_D a une priorité de valeur 4 et ne peut préempter aucun des autres threads. Les résultats sont très II.- Comparaison du nombre de commutations de contexte et du débit Cas Messages Commutations de contexte Cas-1 : Priorités uniques 9 18 Cas-2 : Seuil de préemption 9 4 Mesures Cas-1 (Priorités Uniques) Cas-2 (seuil de préemption) Ratio (Cas-1 vs Cas-2) Commutations de contexte 18 4 450 % 1 801 impulsions du timer 964 impulsions du timer 186 % Messages envoyés 9 9 Pas de changement Messages reçus 9 9 Pas de changement Temps écoulé Dans cet exemple, la comparaison des mesures en débit et en surcharge montre clairement les avantages de l'ordonnancement du seuil de préemption Dans le Cas-1, le Thread_D commence à envoyer ses messages vers chaque file d'attente, mais dès qu'il a envoyé le premier message, le Thread_A prend la main pour le récupérer. Pourquoi ? Parce que ce Thread_A est affecté d'une priorité plus haute que le Thread_D et que le Thread_A est désormais prêt à s'exécuter puisque la file d’attente qu’il surveillait n’est plus vide. Une fois que le Thread_A a lu son message, il se met en pause puisque sa file d’attente est vide, et le Thread_D reprend la main. Le Thread_D envoie maintenant un message au Thread_B, qui préempte immédiatement le Thread_D, et ainsi de suite pour les différents de ceux obtenus dans le Cas-1. En effet, ici, nous n’observons que quatre commutations de contexte, contre 18 pour le Cas-1. Si l’on prend en compte un cycle complet, nous pouvons décompter le nombre d’impulsions d’horloge qui se sont produites durant ce cycle. Dans le Cas-1, on dénombre 1 801 interruptions, alors que le Cas-2 en compte seulement 964. Si l’application considérée était un système émetteur de messages, nous pourrions donc observer une amélioration significative des performances et du débit global grâce à l’utilisation de l’ordon nancement du seuil de préemption (tableau II). n Windows Embedded 8 Standard Windows Embedded 8 Standard réduit les cycles de développement et permet aux fabricants de dispositifs de créer des produits différenciés et haut de gamme Avantages clés • Exécutez vos applications métier Windows existantes ou créez une nouvelle expérience avec les applications Windows 8. • Proposez une expérience d'utilisation immersive et naturelle avec l'interaction tactile multipoint et Kinect pour Windows. • Améliorez la connectivité et la disponibilité avec des technologies de réseau de pointe et une meilleure gestion de l'alimentation. • Créez une image personnalisée du système d'exploitation pour fournir les fonctionnalités nécessaires à l'appareil. à leurs clients. • Fabriquez des appareils spécialisés à partir de technologies de confiance Utilisez les technologies de sécurité renforcée pour protéger votre appareil, vos données et votre réseau. • Contribuez à proposer une expérience cohérente avec des capacités de verrouillage améliorées. • Accédez aux systèmes informatiques et au cloud pour des appareils toujours connectés aux informations essentielles. • Gérez vos appareils avec Microsoft System Center et Windows Embedded Device Manager. • Améliorez le temps de mise sur le marché grâce à des outils de développement système de pointe. qui enchanteront les clients et se démarqueront de la concurrence. Il permet de proposer des solutions métier optimisées pour les systèmes intelligents, et donc de transformer vos données en un avantage concurrentiel durable. Pour en savoir plus, rendez-vous sur www.windowsembedded.com/we8standard. Créez des appareils différenciés S'appuyant sur les dernières innovations de Microsoft, les fabricants peuvent fournir une expérience d'utilisation plus immersive, naturelle et hautement personnalisable afin de distinguer leurs appareils. Les fabricants d'appareils peuvent offrir une expérience de marque unique à tous les niveaux du dispositif et offrir ainsi une expérience d'utilisation exceptionnelle et très personnalisée Windows Embedded 8 Standard vous apporte les dernières innovations technologiques de Windows 8 pour vous aider à protéger les informations sensibles de vos clients sur vos appareils spécialisés. En s'appuyant sur Windows Embedded 8 Standard, les fabricants peuvent tirer parti de ses fonctionnalités pour garantir la fiabilité de leurs appareils et permettre aux utilisateurs de maîtriser la configuration en cours d’utilisation. Activez vos outils d’analyse décisionnelle En s'appuyant sur Windows Embedded 8 Standard, les fabricants peuvent proposer des systèmes intelligents optimisés. Microsoft Active Directory simplifie la gestion des identités et des accès au niveau de l'entreprise. Les appareils spécialisés peuvent être gérés efficacement comme des PC Windows, et se connecter à Windows Azure et Windows Server. Leurs données peuvent ainsi être exploitées de manière optimale pour offrir un réel avantage concurrentiel. 10 ANS Téléchargez Windows Embedded 8 Standard Release Preview sur le site : www.windowsembedded.com/we8standard Les produits Windows Embedded bénéficient d'un programme de support leader de 10 ans et restent disponibles pendant 15 ans.