Le SMP en C ANSI avec LabWindows/CVI
Introduction
Le multitraitement symétrique, aussi appelé SMP (pour Symmetric Multiprocessing), est une caractéristique du système d’exploitation qui permet aux processeurs multicœurs d’exécuter une unique instance d’un système d’exploitation, de se connecter à une mémoire principale commune, et d’exécuter du code en parallèle. Avec cette technologie, vous pouvez répartir des threads entre les processeurs pour équilibrer la charge de travail de façon efficace. La plupart des systèmes d’exploitation polyvalents comme Windows, Linux et Mac OS supportent le SMP. Toutefois, le support du SMP par les OS temps réel (RTOS) n’est pas anodin car le comportement déterministe d’un RTOS doit être préservé pour satisfaire les contraintes de synchronisation temps réel tout en distribuant les threads sur les différents processeurs. Comptant parmi les quelques RTOS à supporter le SMP, le système d’exploitation utilisé par les cibles ETS de LabWindows/CVI Real-Time permet désormais de profiter pleinement des nouveaux matériels multicœurs. Ce tutorial décrit la façon dont vous pouvez utiliser le support du SMP dans Windows à l’aide du SDK Windows. Il traite aussi de l’utilisation du Module LabWindows/CVI 8.5 Real-Time pour mettre en œuvre des applications temps réel hautes performances sur des systèmes multicœurs.
Table des matières
Introduction
Avec le SMP, les divers processeurs (ou cœurs) d'un système sont affectés à certains "pools". Une fois qu'un processeur est affecté à un pool, vous pouvez affecter les threads à exécuter sur les processeurs spécifiques d'un pool. Par conséquent, vous pouvez configurer un pool pour gérer les threads du système d'exploitation tandis qu'un autre pool gère les threads de votre application. Ainsi, le SMP vous octroie un contrôle complet de la distribution de charge d'un système multiprocesseur. Idéalement, un système d'exploitation qui met en œuvre un modèle SMP ne se contente pas d'offrir la flexibilité d'affecter des threads à des cœurs spécifiques mais il intègre aussi une logique pour équilibrer la charge des threads sur plusieurs cœurs.
Pour profiter pleinement de la puissance de traitement supplémentaire d'un système multicœur, le logiciel doit être écrit de sorte que l'exécution puisse être divisée entre les différentes entités de traitement disponibles. Selon la loi d'Amdahl, l'augmentation des performances d'une application sur un processeur multicœur dépend du degré de parallélisme du logiciel en question. Par conséquent, si votre application contient un grand nombre de sections qui doivent s'exécuter en série, l'accroissement des performances sur un système multicœur sera minimal. Toutefois, une application qui est architecturée pour être multithread par nature, voit une augmentation significative de ses performances sur un système multicœur.
Pour en savoir plus :
Tutorial détaillé sur le multithreading dans LabWindows/CVI
Équilibrage automatique de charge
Dans un système multicœur, la responsabilité de distribuer les threads entre les différents cœurs incombe au système d'exploitation. Par défaut, les systèmes d'exploitation standards mettent en œuvre un équilibrage automatique de charge. Cela signifie que le scheduler de threads de l'OS affecte automatiquement le prochain thread à exécuter au CPU le plus approprié. Cette affectation se fait en fonction de divers facteurs, dont bien sûr, le niveau d'utilisation des divers processeurs. En autorisant le système d'exploitation à prendre la responsabilité d'équilibrer la charge automatiquement, vous avez la certitude que votre application obtiendra le meilleur temps d'exécution possible.
Avec un OS temps réel (RTOS), l'équilibrage automatique de charge dépend des priorités des threads. En effet, il faut permettre aux threads temps critique d'interrompre les threads de plus basse priorité. L'OS temps réel gère les décisions concernant le cœur à utiliser au moment d'effectuer la planification des threads de même priorité. Cette planification s'appuie généralement sur un algorithme de permutation circulaire (round robin). Le support du multithread et du multitâche dans un OS temps réel est le fondement qui permet au RTOS de tirer parti des systèmes multicœurs. Le Module LabWindows/CVI 8.5 Real-Time inclut l'extension RT de NI pour le SMP, ce qui ajoute le support multicœur au système d'exploitation temps réel ETS.

Figure n°1. Avec le support de LabWindows/CVI Real-Time pour SMP, vous obtenez un équilibrage de charges automatique sur les divers cœurs.
Affinité processeur sur les systèmes Windows et temps réel
Pour accroître encore davantage les performances et la fiabilité des systèmes Windows ou temps réel, vous pouvez affecter des boucles cadencées ou des threads à des cœurs spécifiques. Avec LabWindows/CVI 8.5, vous avez la possibilité de contrôler par programmation les pools et les attributions de threads.
SMP sur Windows
Sous Windows, la gestion de l'affinité processeur permet potentiellement d'optimiser les performances du cache. Plus particulièrement, en regroupant sur un même processeur les threads qui ont accès aux mêmes données, vous pouvez réduire significativement le nombre d'invalidations de données (cache-miss) qui surviennent lorsque les threads sont répartis sur plusieurs cœurs.
En utilisant les fonctions de gestion d'affinité processeur qui sont déclarées dans le fichier winbase.h, vous pouvez contrôler quels cœurs sont disponibles pour le système d'exploitation et ceux réservés à des threads spécifiques. Sous Windows, il existe deux fonctions principales qui servent à modifier la disponibilité des cœurs.
| BOOL WINAPI SetProcessAffinityMask(
__in HANDLE hProcess, __in DWORD_PTR dwProcessAffinityMask ); |
Un masque d'affinité est un vecteur de bits dans lequel chaque bit représente le processeur sur lequel les threads du processus identifié par la variable hProcess sont autorisés à s'exécuter. La valeur du masque d'affinité utilisée lors de l'appel doit être un sous-ensemble des valeurs du masque d'affinité du système obtenue par la fonction GetProcessAffinityMask.
| DWORD_PTR WINAPI SetThreadAffinityMask(
__in HANDLE hThread, __in DWORD_PTR dwThreadAffinityMask ); |
Le masque d'affinité de thread est un vecteur de bits dans lequel chaque bit représente les processeurs sur lesquels un thread particulier est autorisé à s'exécuter. Le masque d'affinité d'un thread doit être un sous-ensemble du masque d'affinité du processus dont le thread est issu. Un thread ne peut s'exécuter que sur les processeurs sur lesquels son processus père peut s'exécuter.
Remarque : Sous Windows, définir un masque d'affinité pour un processus ou un thread peut aboutir à des threads qui reçoivent moins de temps processeur parce qu'on oblige le système à exécuter les threads que sur certains processeurs. Dans la plupart des cas, il est préférable de laisser le système d'exploitation Windows choisir le processeur à utiliser.
SMP sur un système temps réel
Dans les applications temps réel, l'affinité processeur permet de gagner en performances et en fiabilité. En effet, vous pouvez dédier l'un des cœurs du processeur à un thread de contrôle temps critique et isoler ce dernier des threads moins prioritaires qui s'exécutent alors sur les autres cœurs. Vous utilisez les fonctions de multitraitement symétrique de la bibliothèque Real-Time Utility pour affecter les threads aux CPU d'un système temps réel.

Figure n°2. Le Module LabWindows/CVI Real-Time offre des fonctions pour affecter un thread temps critque à un cœur particulier.
Pour ajouter des processeurs à un pool, utilisez la fonction ConfigureProcessorPool. Grâce à cette fonction, vous pouvez spécifier un masque de processeur, qui détermine quels processeurs figurent dans le pool en question.
Après la configuration d'un pool de processeurs, vous pouvez affecter certains threads à ce pool avec la fonction SetProcessorAffinitiyForThread. En invoquant cette fonction, vous pouvez faire en sorte qu'un thread ne s'exécute que sur un processeur en particulier. Cela permet aux autres processeurs d'exécuter le reste des threads de priorité plus basse de l'application.
Notez que configurer un thread pour qu'il fonctionne sur un processeur particulier ne réserve pas pour autant ce dernier à l'usage exclusif de ce thread. C'est seulement en combinant les fonctions ConfigureProcessorPool et SetProcessorAffinityForThread que vous pouvez retirer un processeur du pool système et configurer un thread pour qu'il s'y exécute. Une fois que vous avez retiré une CPU du pool système, l'unité en question exécute uniquement les threads que vous lui affectez.

Figure n°3. Utilisez la fonction SetProcessorAffinityforThread pour affecter un thread à un cœur particulier.
Mettre au point des applications multicœurs
Au fur et à mesure que les applications se complexifient, il est important de comprendre, au niveau le plus bas, la façon dont le code s'exécute sur le système temps réel. Ajouter des cœurs au système ne fait qu'amplifier cette complexité. Le Toolkit NI Real-Time Execution Trace dans sa version 2.0 offre une représentation visuelle de l'exécution des fonctions et des threads sur les systèmes mono et multicœurs. Cela permet d'identifier rapidement les sections de code source sur lesquelles vous pouvez agir efficacement et détecter des comportements indésirables tels que : contention de ressources, allocations mémoire inefficaces ou inversions de priorités.

Figure n°4. Le Toolkit Real-Time Execution Trace 2.0 offre des caractéristiques de mise au point multicœur.
Vous pouvez aussi utiliser le Real-Time On-Screen CPU Monitor des cibles LabWindows/CVI 8.5 Real-Time pour contrôler l'utilisation de l'unité centrale. Cet utilitaire affiche des informations -- telles que charge totale, le pourcentage d'interruption (ISR, Interrupt Service Request) et l'utilisation des CPU par les threads. L'affichage se fait directement sur un écran qu'il suffit de connecter à la cible temps réel.

Figure n°5. Le Real-Time On-Screen CPU Monitor est disponible sur les cibles LabWindows/CVI 8.5 Real-Time.
Pour en savoir plus :
Mettre au point des applications C ANSI multicœurs avec LabWindows/CVI
Garantir une disponibilité multicœur
Alors que les sociétés migrent leurs applications basées sur une pile logicielle monothread vers une autre qui s'appuie sur un modèle multihread, elles doivent vérifier que chaque couche de la pile en question est "prête-au-multicœur". Ce processus, en plus d'être chronophage, peut consommer pas mal de ressources. Un des avantages du Module LabWindows/CVI 8.5 Real-Time est que la pile logicielle qu'il propose satisfait tous les besoins en termes de support multicœur, y compris : un langage de programmation qui permet de créer des applications multithreads, un accès à des bibliothèques et à des drivers "thread-safe", une capacité à assurer le déterminisme sur des systèmes multicœurs, des outils de mise au point intégrés.
|
Pile logicielle temps réel |
Que signifie être "prêt-au-multicœur" ? |
|
Outil de développement |
Sont disponibles sur OS temps réel. L'outil permet de corriger et d'optimiser le threading des applications développées. Les caractéristiques de mise au point et de traçage de code permettent d'analyser ce qui se passe sur des systèmes multicœurs temps réel. |
|
Bibliothèques |
Les bibliothèques sont "thread-safe" et réentrantes. Elles peuvent donc être utilisées en parallèle. Les algorithmes font des manipulations mémoire locales (in-place). Ils ne causent pas d'allocation mémoire et ne génèrent pas de jitter dans le système. |
|
Drivers matériels |
Les drivers sont multithreads et optimisés. |
|
Système d'exploitation temps réel |
L'OS temps réel supporte le multithread et le multitâche. Il équilibre la charge sur les processeurs des architectures SMP. |
Table n°1. La pile logicielle temps réel se compose d'un outil de développement, de bibliothèques, de drivers matériels et d'un OS temps réel.
On recense un grand nombre de goulets d'étranglement dans les applications parallèles. Cela est généralement dû au fait que les bibliothèques d'interface utilisateur, d'analyse mathématique ou d'interface avec le matériel ne sont pas "thread-safe". Il faut être conscient que si du code non réentrant peut malgré tout être utilisé au sein d'une application multithread, ce dernier ne peut pas s'exécuter en parallèle sur un processeur multicœur. Le code d'une bibliothèque non réentrante est, de fait, une ressource partagée, ce qui occasionne généralement des problèmes en termes de performances. LabWindows/CVI évite ces pièges et propose des bibliothèques d'analyse mathématique réentrantes et des drivers d'E/S matériels, comme NI-DAQmx, qui sont "thread-safe".
Conclusion
Les processeurs multicœurs offrent la possibilité d'avoir, dans la même application, des opérations vraiment parallèles. En créant sous Windows des applications multithread avec LabWindows/CVI et en utilisant les capacités du système d'exploitation à gérer automatiquement la charge processeur, vous pouvez pleinement profiter de cette technologie.
LabWindows/CVI Real-Time permet d'utiliser des processeurs multicœurs hautes performances dans les applications temps réel. Avec le Module Real-Time de LabWindows/CVI 8.5, vous avez la capacité de tirer pleinement avantage de l'affinité processeur en isolant le thread temps critique des autres threads de plus basse priorité.
La simple migration de votre application LabWindows/CVI ou LabWindows/CVI Real-Time vers un système multiprocesseur ne garantit pas que les performances s'en trouvent automatiquement accrues. Pour atteindre des performances optimales sur une machine multiprocesseur, l'application doit être multithread ou rendue multithread. Il n'y a donc qu'en concevant précautionneusement l'architecture logicielle de votre application que vous pourrez pleinement profiter du SMP.
Ressources supplémentaires sur la programmation multicœur C ANSI
- Lisez des documents supplémentaires sur le multithreading C ANSI dans LabWindows/CVI
- Apprenez à mettre au point des applications C ANSI multicœurs avec LabWindows/CVI
- Visualisez la présentation multimédia intitulée "Optimiser les performances multicœurs en C ANSI avec LabWindows/CVI"
- En savoir plus sur LabWindows/CVI
La marque LabWindows est utilisée sous licence Microsoft Corporation. Linux® est la marque déposée de Linus Torvalds aux États-Unis et dans d'autres pays.
Législation
Ce tutorial a été développé par National Instruments. Bien qu'un support technique puisse être fourni pour ce tutorial, il n'a pas été complètement testé ni vérifié, et NI ne garantit pas sa qualité, ni qu'il continuera à être supporté pour les nouvelles versions des produits et drivers qui y sont rattachés. CE TUTORIAL EST FOURNI "EN L'ÉTAT" SANS GARANTIE D'AUCUNE SORTE, ET EST SUJET À CERTAINES RESTRICTIONS COMME PLUS SPÉCIFIQUEMENT DÉTERMINÉES DANS LES CONDITIONS D'UTILISATION DE NI.COM (http://ni.com/legal/termsofuse/unitedstates/us/).
