Luxembourg - Drapeau Luxembourg

Incoterms :DDP
Tous les prix incluent les taxes et les droits de douane pour les modes d'expédition sélectionnés.

Confirmez votre choix de devise:

Euros
Livraison gratuite pour la plupart des commandes supérieures à 50 € (EUR)

Dollars US
Livraison gratuite pour la plupart des commandes supérieures à $60 (USD)

Bench Talk pour les ingénieurs concepteurs

Mouser Blog | France

rss

Le blog officiel de Mouser Electronics


Optimisation des logiciels pour les microprocesseurs ARM multi-cœurs Michael Parks

(Source : Virtual Art Studio – stock.adobe.com ; généré par IA.)

 

Les microprocesseurs multicœurs ARM® marquent une avancée significative dans la technologie des systèmes embarqués. Ils permettent d’effectuer des tâches plus complexes, d’améliorer les performances des applications et de réduire la consommation d’énergie. Dans le présent article de blog, nous passerons en revue les différentes configurations de microprocesseurs ARM multicœurs, puis nous examinerons quelques stratégies d’optimisation des performances de ces microprocesseurs dans les systèmes embarqués.

Profils d’architecture ARM Cortex

L’architecture ARM est réputée pour son efficacité et ses performances. Elle est très répandue dans diverses applications telles que les smartphones et les systèmes de contrôle industriel. Les cœurs ARM se déclinent en une variété de configurations d’architecture pour constituer entre autres les familles de processeurs Cortex-A, Cortex-R et Cortex-M. Chacune de ces familles est destinée à des applications différentes.

  • Cortex-A (processeurs applicatifs) : les processeurs Cortex-A sont conçus pour des systèmes d’exploitation hautes performances et riches en fonctionnalités comme Android ou Linux. Ils se destinent donc surtout aux marchés des smartphones, des tablettes, des équipements réseau et des systèmes industriels haut de gamme.
  • Cortex-R (processeurs temps réel) : les processeurs Cortex-R privilégient les temps de réponse déterministes et la prévisibilité pour les applications en temps réel. Ils sont souvent utilisés dans l’automatisation industrielle, les unités de contrôle du moteur, la robotique et les systèmes critiques pour la sécurité, notamment dans l’électronique automobile et l’avionique.
  • Cortex-M (microcontrôleurs) : les processeurs Cortex-M privilégient la faible consommation d’énergie, la rentabilité et la flexibilité pour convenir à un large éventail d’applications embarquées (dispositifs wearables, capteurs, appareils domestiques intelligents, applications IoT, etc.).

En permettant le traitement parallèle et la gestion efficace des données, les configurations multicœurs offrent des performances améliorées. Vu de l’extérieur, un processeur multicœur peut être considéré de deux façons : d’une part comme une unité unique ou un cluster, soit par le concepteur du système, soit par un système d’exploitation capable d’effectuer l'abstraction des ressources sous-jacentes de la couche d’application ; d’autre part comme plusieurs clusters dont chaque cluster contient plusieurs cœurs.

Les processeurs hautes performances Cortex-A peuvent utiliser des clusters pour améliorer les performances et l’efficacité énergétique. Par exemple, certains systèmes sur puce (SoC) basés sur des cœurs Cortex-A peuvent mettre plusieurs cœurs en cluster avec des caches et des contrôleurs de mémoire partagés. Les processeurs des familles Cortex-R et Cortex-M sont principalement axés sur les performances en temps réel pour les premiers et sur la faible consommation d’énergie pour les seconds. Ils n’implémentent généralement pas de clusters au sens courant du terme. Ils peuvent disposer de configurations multicœurs, mais ces cœurs fonctionnent indépendamment, sans les ressources partagées associées aux architectures en cluster.

De nos jours, même les plateformes de microcontrôleurs bon marchés (p. ex. le Raspberry Pi RP2040) reposent sur deux cœurs M0+. Les configurations matérielles à multiples cœurs sont de plus en plus répandues et ne sont plus l’exclusivité de produits coûteux. Cependant, ce type de matériel peut poser quelques problèmes. Par exemple, quelle que soit la qualité de la conception du matériel, un code mal écrit peut avoir une incidence négative sur le fonctionnement du système.

Stratégies de programmation

Dans les paragraphes suivants, vous trouverez des conseils pour programmer des logiciels efficaces pour les microprocesseurs ARM multicœurs.

Identifier le parallélisme des tâches

Une programmation multicœur réussie repose sur l’identification des opportunités d’exécution parallèle dans votre application. Pour ce faire, commencez par rechercher des tâches indépendantes qui peuvent être exécutées simultanément sans dépendances de données. En voici quelques exemples.

  • Traitement des données des capteurs : plusieurs cœurs peuvent gérer simultanément les données de différents capteurs.
  • Traitement du signal : le filtrage, le calcul de la transformée de Fourier rapide (TFR) et d’autres algorithmes peuvent être répartis sur plusieurs cœurs de façon à diviser les calculs gourmands en ressources matérielles en parties plus simples à gérer.
  • Tâches de l’interface utilisateur : un cœur peut gérer les interactions avec l’utilisateur tandis qu’un autre gère le traitement en arrière-plan.

Choisir un modèle de programmation parallèle

Une fois que vous avez identifié le parallélisme, sélectionnez un modèle de programmation approprié pour coordonner les tâches entre les cœurs. Vous avez le choix entre plusieurs modèles courants.

  • Principal/secondaire : un cœur (principal) distribue les tâches aux autres cœurs (secondaires) et gère la communication. Cette approche est simple, mais elle est susceptible de créer un goulot d’étranglement pour le cœur maître.
  • Multithreading : chaque cœur exécute son propre thread, ce qui permet un parallélisme plus fin. Une synchronisation rigoureuse est nécessaire pour éviter les situations de concurrence.
  • Transmission de messages : les cœurs communiquent en envoyant des messages, ce qui permet une distribution flexible des tâches et un équilibrage dynamique des charges de travail.

Adopter un codage efficace

Certaines opérations logicielles dépendent du cœur sur lequel le code est exécuté. Par exemple, l’initialisation globale est généralement effectuée par un code exécuté sur un seul cœur, suivi d’une initialisation locale sur tous les cœurs. Il suffit de vérifier deux emplacements pour identifier le cœur qui exécute le code.

  • Multi-Processor Affinity Register (MPIDR_EL1) : ce registre indique quel cœur exécute le code, aussi bien dans un cluster qu’au sein d’un système multicluster.
  • U-bit : certaines configurations de processeur indiquent s’il s’agit d’un cluster monocœur ou multicœur.

Il est également recommandé de tenir compte des éléments de conception suivants pour optimiser les logiciels.

  • Modularité du code : il est essentiel d’écrire un code modulaire. Vous y gagnerez en lisibilité tout en simplifiant la gestion de la base de code, le débogage et la maintenance.
  • Gestion de la mémoire : une utilisation efficace de la mémoire est essentielle dans les systèmes embarqués. Les développeurs doivent être attentifs à l’utilisation de la pile et du tas, éviter les fuites de mémoire et utiliser l’accès direct à la mémoire (DMA) pour les opérations utilisant de gros volumes de données.
  • Efficacité énergétique : dans le cas d’appareils alimentés par batterie, il est indispensable d’optimiser le code pour atteindre une efficacité énergétique maximale. Plusieurs techniques peuvent être utilisées à cet effet, notamment l’utilisation de modes veille, la réduction de la fréquence d’horloge et l’optimisation de la gestion des interruptions.

Tirer parti de la concurrence

La concurrence des tâches est un concept essentiel lorsqu’il s’agit de microprocesseurs multicœurs, car elle permet d’utiliser efficacement plusieurs cœurs pour l’exécution parallèle des tâches et ainsi améliorer les performances globales du système. En exécutant les tâches en concurrence (c’est-à-dire simultanément), le système est en mesure de gérer davantage de processus simultanément, ce qui réduit la latence et augmente la réactivité des applications sensibles au temps. La concurrence permet en outre une meilleure gestion des ressources et garantit ainsi une répartition uniforme des volumes de données de calcul sur les cœurs de façon à éviter l’apparition de goulots d’étranglement et à maximiser l’efficacité.

Voici quelques méthodes pour optimiser la concurrence dans les microprocesseurs multicœurs.

  • Parallélisme des tâches : cette approche consiste à diviser l’application en tâches indépendantes qui peuvent être exécutées simultanément. Elle est pratique pour les applications qui peuvent être décomposées en tâches distinctes et parallélisables.
  • Parallélisme des données : il s’agit d’effectuer la même opération sur plusieurs éléments de données en parallèle. Cette méthode est particulièrement appropriée au traitement des signaux et d’images ainsi qu’à d’autres tâches comparables utilisant de gros volumes de données.
  • Synchronisation : une synchronisation correcte est essentielle pour éviter les situations de concurrence et la corruption des données. Les microprocesseurs ARM proposent divers mécanismes de synchronisation, dont des sémaphores, des mutex et des barrières.
  • Communication interprocesseur (IPC) : les systèmes multicœurs exigent des mécanismes IPC efficaces. La mémoire partagée, la transmission de messages et les signaux d’interruption sont des techniques couramment utilisées. L’exécution simultanée nécessite des mécanismes capables de garantir la cohérence des données et d’éviter les situations de concurrence.
    • Sémaphores : ils permettent de contrôler l’accès aux ressources partagées (comme les blocs de mémoire) de façon à empêcher que des données ne soient modifiées par plusieurs cœurs en même temps.
    • Mutex : ils permettent d’accorder un accès exclusif à une section critique du code afin de garantir que cette partie de code ne puisse être exécutée que par un seul cœur à la fois (voir figure 1).
    • Files d’attente de messages : les cœurs échangent des données en envoyant et en recevant des messages, ce qui facilite la communication asynchrone.

Figure 1 : les mutex sont basés sur des objets et peuvent être considérés comme une méthode de transmission d’une clé pour une ressource partagée verrouillée. Un sémaphore est basé sur un compteur d’entiers et peut être considéré comme un feu de signalisation pour contrôler l’accès. (Source : Auteur)

 

Stratégies d’optimisation

L’optimisation des logiciels est essentielle pour les microprocesseurs multicœurs, car elle a un impact direct sur leurs performances et leur efficacité. Un code correctement optimisé réduit à un minimum les instructions inutiles et utilise efficacement les ressources matérielles telles que la mémoire. Cela permet d’améliorer l’exécution parallèle dans différents cœurs et d’augmenter significativement les performances des systèmes multicœurs tout en réduisant la consommation d’énergie.

Optimisation des logiciels

Voici quelques stratégies d’optimisation des logiciels pour les microprocesseurs multicœurs.

  • Optimisations du compilateur : utilisez les indicateurs d’optimisation du compilateur pour améliorer les performances et réduire la taille du code. Pour ce faire, il est essentiel de comprendre en quoi consistent les compromis entre les niveaux d’optimisation.
  • Profilage et benchmarking : profilez régulièrement l’application pour identifier les goulots d’étranglement. Pour ce faire, des outils comme l’analyseur de performances Streamline d’ARM vous fourniront de précieuses informations.
  • Optimisation du cache : une utilisation efficace du cache peut grandement améliorer les performances. Plusieurs techniques peuvent être utilisées à cet effet, comme le verrouillage du cache pour les sections critiques et l’optimisation des structures de données pour augmenter l’efficacité du cache.

Optimisation du cache et de la mémoire

Les processeurs multicœurs ont souvent des hiérarchies de cache complexes. Or, l’utilisation efficace de ces caches est un facteur clé de performances.

  • Localité des données : regroupez les données fréquemment consultées en mémoire pour améliorer les taux de réussite du cache.
  • Alignement sur les lignes de cache : assurez-vous que les structures de données soient alignées sur les limites des lignes de cache pour favoriser un accès efficace.
  • Réduction à un minimum des faux partages : évitez de placer des données non liées dans la même ligne de cache pour prévenir toute invalidation inutile.
  • Optimisation de l’assemblage : pour les sections de code critiques, envisagez d’utiliser un langage d’assemblage. Vous aurez ainsi un contrôle total du matériel pour des performances maximales.

Tirer parti des fonctionnalités matérielles

Les microprocesseurs ARM multicœurs modernes fournissent souvent des mécanismes assistés par matériel offrant une communication et une synchronisation efficaces. En voici quelques-uns.

  • Périphériques IPC : des canaux matériels dédiés pour un échange de données rapide entre les cœurs.
  • Unités de gestion de la mémoire (MMU) : ce matériel permet de protéger et d’isoler la mémoire répartie sur plusieurs cœurs pour une sécurité et une fiabilité accrues.
  • Protocoles de cohérence du cache : il s’agit de mécanismes gérés par le matériel qui permettent de garantir la cohérence des données réparties dans les caches de différents cœurs.

Débogage, profilage et tests

La mise en œuvre des méthodes d’optimisation des logiciels pour microprocesseurs multicœurs mentionnées ci-dessus devrait aboutir à une nette amélioration des performances et de l’efficacité énergétique. Cependant, l’implémentation de code pour les systèmes multicœurs, et en particulier lorsqu’il s’agit de systèmes embarqués, peut avoir de nombreux effets indésirables. C’est pourquoi tout code doit être testé et mesuré afin de s’assurer de l’efficacité de son exécution sur plusieurs cœurs.

  • Débogueurs compatibles multicœurs : inspectez les états de chaque cœur, les canaux de communication et les primitives de synchronisation à l’aide d’outils de débogage tels que JTAG, SWD ainsi que de fonctionnalités de débogage intégrées fournies par les microprocesseurs ARM.
  • Outils de profilage : identifiez les goulots d’étranglement des performances et évaluez l’utilisation des cœurs afin de répartir les tâches de façon optimale.
  • Tests unitaires : implémentez des tests unitaires pour les composants individuels afin d’en garantir la fiabilité et de faciliter leur maintenance avant leur intégration dans le système global.
  • Tests d’intégration : testez l’interaction entre les différents composants du système. Cette démarche est particulièrement importante dans les environnements multicœurs où les interactions entre les tâches peuvent être complexes.

Conclusion

La programmation de microprocesseurs ARM multicœurs présente quelques difficultés particulières, mais aussi un potentiel d’amélioration significative des performances des systèmes embarqués. En comprenant l’architecture ARM, en identifiant et en planifiant soigneusement les tâches parallèles, en adoptant des méthodes de codage efficaces, en exploitant efficacement le concept de concurrence et en appliquant des stratégies d’optimisation, les développeurs sont en mesure de maximiser les capacités de ces puissants dispositifs multicœurs. Si le présent article offre déjà un aperçu des connaissances de base, la maîtrise de la programmation de microprocesseurs ARM multicœurs requiert une étude approfondie, une expérience pratique et un suivi continu des technologies et méthodologies les plus récentes. ARM propose un guide d’introduction au programmeur ainsi qu’une formation approfondie pour aider les ingénieurs à optimiser leur programmation.



« Retour


Michael Parks's Blog

Tous les auteurs

Voir plus Voir plus
Afficher les articles par date