Comparaison du runtime d'accès concurrentiel aux autres modèles d'accès concurrentiel

Ce document décrit les différences entre les fonctionnalités et modèles de programmation du runtime d'accès concurrentiel et d'autres technologies. Lorsque vous comprenez les avantages du runtime d'accès concurrentiel par rapport à ceux d'autres modèles de programmation, vous pouvez sélectionner la technologie qui répond le mieux aux besoins de vos applications.

Si vous utilisez actuellement un autre modèle de programmation, tel que le pool de threads Windows ou OpenMP, il existe des situations dans lesquelles il peut s'avérer nécessaire d'effectuer une migration vers le runtime d'accès concurrentiel. Par exemple, la rubrique Migration d'OpenMP au runtime d'accès concurrentiel explique les situations dans lesquelles il peut être nécessaire d'effectuer une migration d'OpenMP vers le runtime d'accès concurrentiel. Toutefois, si vous êtes satisfait des performances de votre application et de la prise en charge du débogage actuelle, la migration n'est pas nécessaire.

Vous pouvez utiliser les fonctionnalités et les avantages en termes de performances du runtime d'accès concurrentiel pour compléter votre application existante, qui utilise un autre modèle d'accès concurrentiel. Le runtime d'accès concurrentiel ne peut pas garantir l'équilibrage de charge lorsque plusieurs planificateurs de tâches rivalisent pour la même ressource de calcul. Toutefois, lorsque les charges de travail ne se chevauchent pas, l'effet est minime.

Sections

  • Comparaison de la planification préemptive avec la planification coopérative

  • Comparaison du runtime d'accès concurrentiel avec l'API Windows

  • Comparaison du runtime d'accès concurrentiel avec OpenMP

Comparaison de la planification préemptive avec la planification coopérative

Le modèle préemptif et les modèles de planification coopérative sont deux façons courantes de permettre à plusieurs tâches de partager des ressources de calcul, par exemple des processeurs ou des threads matériels.

Planification préemptive et coopérative

La planification préemptive est un mécanisme tourniquet (round robin) basé sur la priorité qui donne à chaque tâche l'accès exclusif à une ressource de calcul pour une période donnée, puis bascule vers une autre tâche. La planification préemptive est courante dans les systèmes d'exploitation multitâches tels que Windows. La planification coopérative est un mécanisme qui donne à chaque tâche l'accès exclusif à une ressource de calcul jusqu'à ce que la tâche se termine ou qu'elle cède son accès à la ressource. Le runtime d'accès concurrentiel utilise la planification coopérative avec le planificateur préemptif du système d'exploitation pour obtenir une utilisation maximale des ressources de traitement.

Différences entre les planificateurs préemptifs et coopératifs

Les planificateurs préemptifs cherchent à accorder à plusieurs threads un accès égal aux ressources de calcul afin de s'assurer que chaque thread progresse. Sur les ordinateurs dotés de nombreuses ressources de calcul, la garantie d'un accès juste est moins problématique ; en revanche, la garantie d'une utilisation efficace des ressources est plus problématique.

Un planificateur en mode noyau préemptif requiert que le code d'application repose sur le système d'exploitation pour prendre des décisions de planification. Inversement, un planificateur coopératif en mode utilisateur permet au code d'application de prendre ses propres décisions de planification. Étant donné que la planification coopérative permet à l'application de prendre de nombreuses décisions de planification, elle réduit une grande partie de la charge associée à la synchronisation en mode noyau. Un planificateur coopératif diffère en général les décisions de planification au noyau du système d'exploitation lorsqu'il n'a aucun autre travail à planifier. Un planificateur coopératif diffère également les décisions au planificateur du système d'exploitation lorsqu'une opération bloquante est communiquée au noyau mais n'est pas communiquée au planificateur en mode utilisateur. Sur les systèmes d'exploitation qui prennent en charge les threads planifiables en mode utilisateur (UMS, User-Mode Schedulable), le planificateur de runtime d'accès concurrentiel convertit ces opérations bloquantes en opérations bloquantes coopératives.

Planification coopérative et efficacité

Pour un planificateur préemptif, tout travail qui a le même niveau de priorité est égal. Un planificateur préemptif planifie en général les threads dans l'ordre dans lequel ils sont créés. En outre, un planificateur préemptif accorde tour à tour à chaque thread une tranche horaire , en fonction de la priorité des threads. Bien que ce mécanisme soit juste (chaque thread progresse), il a coût en matière d'efficacité. Par exemple, de nombreux algorithmes à calcul intensif ne requièrent pas ce critère de justesse. Au lieu de cela, il est important que les tâches connexes se terminent le plus rapidement possible. La planification coopérative permet à une application de planifier le travail plus efficacement. Par exemple, considérez une application qui a de nombreux threads. La planification de l'exécution simultanée de threads qui ne partagent pas de ressources peut réduire la charge de synchronisation et ainsi augmenter l'efficacité. Une autre méthode efficace pour planifier des tâches consiste à exécuter des pipelines de tâches (où chaque tâche agit sur la sortie de la précédente) sur le même processeur de sorte que l'entrée de chaque étape de pipeline soit déjà chargée dans le cache mémoire.

Utilisation conjointe de la planification préemptive et coopérative

La planification coopérative ne résout pas tous les problèmes de planification. Par exemple, les tâches qui ne cèdent pas équitablement les ressources aux autres tâches peuvent consommer toutes les ressources de calcul disponibles et empêcher d'autres tâches de progresser. Le runtime d'accès concurrentiel utilise les avantages en termes d'efficacité de la planification coopérative pour compléter les garanties de justesse de la planification préemptive. Par défaut, le runtime d'accès concurrentiel fournit un planificateur coopératif qui utilise un algorithme de vol de travail pour répartir efficacement le travail parmi les ressources de calcul. Toutefois, le planificateur du runtime d'accès concurrentiel repose également sur le planificateur préemptif du système d'exploitation pour répartir équitablement les ressources parmi les applications. Vous pouvez également créer des planificateurs et des stratégies de planificateur personnalisés dans vos applications pour obtenir un contrôle affiné sur l'exécution des threads.

[retour en haut]

Comparaison du runtime d'accès concurrentiel avec l'API Windows

L'interface de programmation d'application Microsoft Windows, plus connue sous le nom d'API Windows (et autrefois appelée Win32), fournit un modèle de programmation qui autorise l'accès concurrentiel dans vos applications. Le runtime d'accès concurrentiel tire parti de l'API Windows pour fournir des modèles de programmation supplémentaires qui ne sont pas disponibles à partir du système d'exploitation sous-jacent.

Le runtime d'accès concurrentiel tire parti du modèle de thread API Windows pour exécuter du travail parallèle. Il utilise également les mécanismes de gestion de la mémoire et de stockage de threads locaux de l'API Windows. Sur Windows 7 et Windows Server 2008 R2, il utilise la prise en charge de l'API Windows pour les threads planifiables par l'utilisateur et les ordinateurs qui ont plus de 64 threads matériels. Le runtime d'accès concurrentiel étend le modèle de l'API Windows en fournissant un planificateur de tâches coopératif et un algorithme de vol de travail pour optimiser l'utilisation des ressources de calcul, et en autorisant l'existence de plusieurs instances de planificateur simultanées.

Pour plus d'informations sur l'API Windows, consultez Vue d'ensemble de l'API Windows.

Langages de programmation

L'API Windows utilise le langage de programmation C pour exposer le modèle de programmation. Le runtime d'accès concurrentiel fournit une interface de programmation C++ qui tire parti des fonctionnalités les plus récentes du langage C++. Par exemple, des fonctions lambda fournissent un mécanisme succinct de type sécurisé pour la définition de fonctions de travail parallèle. Pour plus d'informations sur les fonctionnalités de C++ les plus récentes utilisées par le runtime d'accès concurrentiel, consultez Vue d'ensemble du runtime d'accès concurrentiel.

Threads et pools de threads

Le mécanisme d'accès concurrentiel central dans l'API Windows est le thread. On utilise en général la fonction CreateThread pour créer des threads. Bien que les threads soient relativement faciles à créer et à utiliser, le système d'exploitation alloue une durée significative et d'autres ressources pour les gérer. En outre, bien qu'il soit garanti que chaque thread reçoive la même durée d'exécution que tout autre thread au même niveau de priorité, la charge associée requiert la création de tâches suffisamment grandes. Pour les tâches plus petites ou plus affinées, la charge associée à l'accès concurrentiel peut annuler l'avantage offert par l'exécution des tâches en parallèle.

Les pools de threads constituent une manière de réduire le coût de gestion des threads. Les pools de threads personnalisés et l'implémentation de pools de threads fournie par l'API Windows permettent à de petits éléments de travail de s'exécuter efficacement en parallèle. Le pool de threads Windows maintient les éléments de travail dans une file d'attente de type « premier entré, premier sorti » (FIFO). Chaque élément de travail est démarré dans l'ordre dans lequel il a été ajouté au pool.

Le runtime d'accès concurrentiel implémente un algorithme de vol de travail pour étendre le mécanisme de planification FIFO. L'algorithme déplace les tâches qui n'ont pas encore démarré vers des threads qui exécutent des éléments sans travail. Bien que l'algorithme de vol de travail puisse équilibrer les charges de travail, il peut également provoquer le réordonnancement des éléments de travail. Ce processus de réordonnancement peut provoquer le démarrage d'un élément de travail dans un ordre différent de celui dans lequel il a été soumis. Ceci est utile avec les algorithmes récursifs, où il y a davantage de chance que les données soient partagées parmi de nouvelles tâches que parmi des tâches plus anciennes. Le fait d'exécuter les nouveaux éléments en premier permet de réduire le nombre d'accès au cache sans résultat et éventuellement le nombre de défauts de page.

Du point de vue du système d'exploitation, le vol de travail est injuste. Toutefois, lorsqu'une application implémente un algorithme ou une tâche pour une exécution en parallèle, la justesse parmi les sous-tâches n'importe pas toujours. Ce qui importe est la rapidité avec laquelle la tâche globale se termine. Pour d'autres algorithmes, le mécanisme FIFO est la stratégie de planification appropriée.

Comportement sur différents systèmes d'exploitation

Sur Windows XP et Windows Vista, les applications qui utilisent le runtime d'accès concurrentiel se comportent de la même façon, mais les performances des tas sont améliorées sur Windows Vista.

Dans Windows 7 et Windows Server 2008 R2, le système d'exploitation prend davantage en charge l'accès concurrentiel et la montée en charge. Par exemple, ces systèmes d'exploitation prennent en charge les ordinateurs qui ont plus de 64 threads matériels. Une application existante qui utilise l'API Windows doit être modifiée pour tirer parti de ces nouvelles fonctionnalités. Toutefois, une application qui utilise le runtime d'accès concurrentiel utilise automatiquement ces fonctionnalités et ne requiert pas de modification.

Windows 7 et Windows Server 2008 R2 ajoutent la prise en charge des threads UMS. Les threads UMS permettent aux planificateurs en mode utilisateur et au noyau de planifier le travail plus efficacement. Bien que la planification UMS n'abandonne pas la préemption, elle augmente l'efficacité en permettant aux applications et aux bibliothèques d'exécuter la planification coopérative sans transitions de noyau. La planification UMS redonne également le contrôle à l'application lorsqu'un thread est bloqué dans le noyau, afin que l'application puisse exécuter du travail supplémentaire pendant le reste de la tranche horaire. Le runtime d'accès concurrentiel utilise automatiquement la planification UMS lorsqu'elle est prise en charge par le système d'exploitation. Pour plus d'informations sur la planification UMS, consultez Planification en mode utilisateur.

[retour en haut]

Comparaison du runtime d'accès concurrentiel avec OpenMP

Le runtime d'accès concurrentiel autorise divers modèles de programmation. Ces modèles peuvent se chevaucher ou compléter les modèles d'autres bibliothèques. Cette section compare le runtime d'accès concurrentiel avec OpenMP.

Le modèle de programmation OpenMP est défini par une norme ouverte et a des liaisons bien définies aux langages de programmation Fortran et C/ C++. Les versions 2.0 et 2.5 d'OpenMP conviennent aux algorithmes parallèles qui sont itératifs, c'est-à-dire qui effectuent une itération parallèle sur un tableau de données. OpenMP est très efficace lorsque le degré de parallélisme est prédéterminé et qu'il correspond aux ressources disponibles sur le système. Le modèle OpenMP convient particulièrement à l'informatique hautes performances, où les problèmes de calcul à grande échelle sont répartis sur les ressources de traitement d'un seul ordinateur. Dans ce scénario, l'environnement matériel est connu et le développeur peut raisonnablement s'attendre à disposer d'un accès exclusif aux ressources de calcul lors de l'exécution de l'algorithme.

En revanche, d'autres environnements informatique moins contraints peuvent ne pas constituer une bonne correspondance pour OpenMP. Par exemple, les problèmes récursifs (tels que l'algorithme de tri rapide ou la recherche dans une arborescence de données) sont plus difficiles à implémenter à l'aide d'OpenMP. Le runtime d'accès concurrentiel complète les fonctions d'OpenMP en fournissant la bibliothèque de modèles parallèles (PPL) et la bibliothèque d'agents asynchrones. Contrairement à OpenMP, le runtime d'accès concurrentiel fournit un planificateur dynamique qui s'adapte aux ressources disponibles et ajuste le degré de parallélisme à mesure que les charges de travail évoluent.

Une grande partie des fonctionnalités du runtime d'accès concurrentiel peuvent être étendues. Vous pouvez également combiner des fonctionnalités existantes pour en composer de nouvelles. Étant donné qu'OpenMP repose sur des directives de compilateur, son extension n'est pas facile.

Pour plus d'informations sur les différences entre le runtime d'accès concurrentiel et OpenMP, ainsi que sur la migration d'un code OpenMP existant pour utiliser le runtime d'accès concurrentiel, consultez Migration d'OpenMP au runtime d'accès concurrentiel.

[retour en haut]

Voir aussi

Référence

Vue d'ensemble de l'API Windows

Concepts

Concurrency Runtime

Vue d'ensemble du runtime d'accès concurrentiel

Bibliothèque de modèles parallèles

Bibliothèque d'agents asynchrones

Autres ressources

OpenMP in Visual C++

Historique des modifications

Date

Historique

Motif

Juillet 2010

Ajout d'informations sur la migration et l'interopérabilité.

Améliorations apportées aux informations.