Comment programmer l'allocation des threads sur les processeurs multicœurs?

13

Je voudrais expérimenter avec des threads sur un processeur multicœur, par exemple pour créer un programme qui utilise deux threads différents qui sont exécutés par deux cœurs de processeur différents.

Cependant, il n'est pas clair pour moi à quel niveau les threads sont alloués aux différents cœurs. Je peux imaginer les scénarios suivants (selon le système d'exploitation et l'implémentation du langage de programmation):

  1. L'allocation des threads est gérée par le système d'exploitation. Les threads sont créés à l'aide d'appels système du système d'exploitation et, si le processus s'exécute sur un processeur multicœur, le système d'exploitation essaie automatiquement d'allouer / planifier différents threads sur différents cœurs.
  2. L'allocation des threads est gérée par l'implémentation du langage de programmation. L'allocation de threads à différents cœurs nécessite des appels système spéciaux, mais les bibliothèques de threads standard du langage de programmation gèrent automatiquement cela lorsque j'utilise l'implémentation de thread standard pour ce langage.
  3. L'allocation des threads doit être programmée explicitement. Dans mon programme, je dois écrire du code explicite pour détecter le nombre de cœurs disponibles et allouer différents threads à différents cœurs en utilisant, par exemple, les fonctions de bibliothèque.

Pour rendre la question plus précise, imaginez que j'ai écrit mon application multi-thread en Java ou C ++ sur Windows ou Linux. Mon application verra-t-elle et utilisera-t-elle plusieurs cœurs comme par magie lorsqu'elle est exécutée sur un processeur multicœur (car tout est géré soit par le système d'exploitation, soit par la bibliothèque de threads standard), ou dois-je modifier mon code pour connaître les cœurs multiples ?

Giorgio
la source

Réponses:

11

Mon application verra-t-elle et utilisera-t-elle plusieurs cœurs comme par magie lorsqu'elle est exécutée sur un processeur multicœur (car tout est géré soit par le système d'exploitation, soit par la bibliothèque de threads standard), ou dois-je modifier mon code pour connaître les cœurs multiples ?

Réponse simple: Oui, il sera généralement géré par le système d'exploitation ou la bibliothèque de threads.

Le sous-système de threading du système d'exploitation attribuera des threads aux processeurs en priorité (votre option 1). En d'autres termes, lorsqu'un thread a terminé son exécution pour son allocation de temps ou ses blocs, le planificateur recherche le prochain thread de priorité la plus élevée et l'affecte à la CPU. Les détails varient d'un système d'exploitation à l'autre.

Cela dit, les options 2 (gérées par le langage de programmation) et 3 (explicitement) existent. Par exemple, la bibliothèque de tâches et async / wait dans les versions récentes de .Net offrent au développeur un moyen beaucoup plus facile d'écrire du code parallélisable (c'est-à-dire qui peut s'exécuter simultanément avec lui-même). Les langages de programmation fonctionnels sont intrinsèquement parallélisables et certains runtimes exécuteront différentes parties du programme en parallèle si possible.

Comme pour l'option 3 (explicitement), Windows vous permet de définir l'affinité du thread (en spécifiant sur quels processeurs un thread peut s'exécuter). Cependant, cela n'est généralement pas nécessaire dans tous les systèmes critiques, à l'exception des plus rapides. L'allocation efficace du thread au processeur dépend fortement du matériel et est très sensible aux autres applications s'exécutant simultanément.

Si vous souhaitez expérimenter, créez une tâche longue et gourmande en ressources processeur, comme la génération d'une liste de nombres premiers ou la création d'un ensemble Mandelbrot. Créez maintenant deux threads dans votre bibliothèque préférée et exécutez les deux threads sur une machine multiprocesseur (en d'autres termes, à peu près tout ce qui a été publié ces dernières années). Les deux tâches doivent se terminer à peu près en même temps car elles sont exécutées en parallèle.

akton
la source
Merci pour l'explication (+1). Mon programme de test est une implémentation de tri par fusion. Dans la phase de partage, je veux créer différents threads tant qu'il y a des cœurs disponibles. Par exemple, avec deux cœurs, chaque moitié d'un tableau serait triée par un thread / noyau différent. Pendant la fusion, les threads superflus seraient alors joints / terminés.
Giorgio
Le tri est difficile à paralléliser de cette manière si les données sont distribuées de manière aléatoire. Oui, vous pouvez le décomposer puis trier chaque partie dans un thread différent, mais vous devez finalement fusionner toutes les parties ensemble, de toute façon. Si les threads partagent des structures de données, vous pouvez également rencontrer des problèmes de contention ou de verrouillage. Je ne dis pas que le tri ne peut pas bénéficier du filetage mais ce ne sera pas une amélioration linéaire des performances.
akton
Les deux moitiés d'un tableau peuvent être triées indépendamment car aucune donnée n'est partagée. Seul le premier fractionnement et la dernière fusion devront être effectués par un thread manipulant l'ensemble du tableau ou de la liste contenant les données. Cela signifie qu'une analyse complète des données ne peut pas être exécutée en parallèle; toutes les analyses restantes le peuvent.
Giorgio
Bien sûr, je considère également vos exemples comme de bons candidats. Je suis juste plus familier avec le tri par fusion pour le moment (et j'en ai implémenté une version non parallèle), ce qui pourrait (peut-être) rendre le tri par fusion plus adapté à ma première tentative.
Giorgio
2
J'ajouterais à cette réponse que de bons systèmes d'exploitation sont suffisamment intelligents pour équilibrer le coût de la tâche dans une tranche de temps sur un autre processeur ou noyau avec celui d'une famine à court terme. Sur les architectures où cela compte, le résultat a tendance à ressembler à l'affinité automagique. Le système d'exploitation a été conçu pour exécuter tous les travaux le plus rapidement possible, et vous pouvez vous tirer une balle dans le pied en attachant des fils aux noyaux et en empêchant sa capacité de prendre ces décisions.
Blrfl
-1

J'avais une fois un énorme environnement SGI IRIX. Juste pour le plaisir, j'ai écrit un petit programme java multi-thread (qui ne faisait que consommer des cycles CPU) et y ai créé 12 threads. Le travail s'est étendu sur 12 CPU dans l'architecture NUMA. Peut-être que je vais rechercher le programme et l'exécuter sur le Dell R910s et vérifier ..

P. Prabhakar
la source
3
Cette réponse n'ajoute vraiment pas grand-chose à la réponse existante. Peut-être que si vous expliquiez pourquoi la JVM sur le système SGI a alloué des threads au noyau ...
Jay Elston