Combien d'efforts devons-nous consacrer à la programmation de plusieurs cœurs?

12

Les processeurs obtiennent de plus en plus de cœurs ces jours-ci, ce qui me laisse perplexe ...

Devrions-nous, programmeurs, nous adapter à ce comportement et consacrer plus d'efforts à la programmation pour plusieurs cœurs?

Dans quelle mesure devons-nous le faire et l'optimiser? Fil? Affinité? Optimisations matérielles? Autre chose?

Tamara Wijsman
la source

Réponses:

15

Peu importe à quel point vous êtes bon, il est peu probable que vous arriviez à un meilleur schéma de gestion des threads, etc. que les équipes développant le langage et le compilateur dans lesquels vous écrivez votre code.

Si vous avez besoin que votre application soit multi-thread, créez les threads dont vous avez besoin et laissez le compilateur et le système d'exploitation poursuivre leurs tâches.

Vous devez savoir comment ces threads sont gérés afin de pouvoir utiliser au mieux les ressources. Ne pas créer trop de threads est une chose qui me vient à l'esprit à titre d'exemple.

Vous devez également être conscient de ce qui se passe (voir le commentaire de Lorenzo) afin de pouvoir fournir des conseils à la gestion des threads (ou la remplacer dans des cas particuliers), mais j'aurais pensé que ceux-ci seraient rares.

ChrisF
la source
3
Mais un thread qui saute continuellement d'un cœur à un autre aura des pénalités de performances (en raison du cache de processeur de premier et de deuxième niveau manquant), en particulier dans les architectures où deux matrices physiques distinctes sont utilisées. Dans le code intensif multithread, l'affinité est une bonne chose.
Wizard79
@Lorenzo - Dans ce cas, vous devrez voir si vous pouvez attacher le fil à un seul noyau - ce qui est peut-être un cas spécial - mais intéressant.
ChrisF
1
Ne serait-ce pas une décision plutôt étrange que le système d'exploitation commute en contexte un thread actif d'un noyau à un autre?
JBRWilkinson
Je suis d'accord avec @JBRWilkinson, l'affinité des threads me semble être un travail OS.
Collin
1
@JBRWilkinson Sous linux (et je pense que la plupart des OS) les threads sautent entre les cœurs tout le temps. La première raison est que vous avez globalement beaucoup plus de threads que de cœurs. Et si certains fils meurent, vous devez équilibrer. La deuxième raison est que de nombreux threads dorment. Et lorsque certains se réveillent, le noyau peut penser qu'un noyau a plus de charge que d'autres et déplacer un thread, souvent votre thread de calcul monopolisant le processeur. Ensuite, 2 threads de traitement monophasé s'exécutent sur le même noyau jusqu'à ce que le noyau en recule d'un. Si vous divisez un travail volumineux en parties exactement numériques, vous souhaitez définir l'affinité du thread.
Goswin von Brederlow
5

Je suis un programmeur .NET, et je sais que .NET a une abstraction de haut niveau pour le multithreading appelée Tâches. Il vous protège d'avoir à en savoir trop sur la façon de faire du multithreading approprié contre le métal. Je suppose que d'autres plates-formes de développement actuelles ont des abstractions similaires. Donc, si vous allez faire quoi que ce soit avec le multithreading, j'essaierais de travailler à ce niveau si possible.

Maintenant, à la question de savoir si vous devez vous soucier du multithreading dans votre application particulière. La réponse à cette question dépend beaucoup de l'application que vous écrivez. Si vous écrivez une application qui traite sur des milliers (ou plus) de choses indépendantes, et que ce traitement peut être effectué en parallèle, vous tirerez certainement un avantage du multithreading. Cependant, si vous écrivez un écran de saisie de données simple, le multithreading peut ne pas vous rapporter beaucoup.

À tout le moins, vous devez vous préoccuper du multithreading lorsque vous travaillez sur une interface utilisateur. Vous ne voulez pas déclencher une opération de longue durée à partir de l'interface utilisateur et la faire cesser de répondre parce que vous avez détourné le thread d'interface utilisateur pour effectuer cette opération. Déclenchez un fil d'arrière-plan et donnez au moins à l'utilisateur un bouton Annuler pour qu'il n'ait pas à attendre qu'il se termine s'il a fait une erreur.

RationalGeek
la source
5

Au pays d'Objective-C et de Mac OS X et iOS, les frameworks (comme beaucoup d'autres) sont écrits pour tirer parti de ces augmentations des cœurs de processeur et présenter au développeur une belle interface pour les utiliser.

Un exemple sur Mac OS X et iOS est la répartition Grand Central. Il y a des ajouts à libc(je crois) pour faciliter le multi-threading basé sur la file d'attente. Ensuite, les frameworks Cocoa et Foundation (entre autres) sont écrits au-dessus de GCD, donnant au développeur un accès facile aux files d'attente de distribution et au threading avec très peu de code de plaque de chaudière.

De nombreux langages et frameworks ont des concepts similaires.

Jasarien
la source
5

La partie la plus difficile consiste à diviser votre algorithme gourmand en ressources processeur en morceaux d'exécution pouvant être enfilés.

Ensuite, un thread qui saute continuellement d'un cœur à un autre aura des pénalités de performances (en raison d'un cache de processeur de premier et de deuxième niveau manquant), en particulier dans les architectures où deux matrices physiques distinctes sont utilisées. Dans ce cas, l'affinité du noyau de thread est une bonne chose.

Wizard79
la source
3

Nous sommes maintenant (octobre 2010) dans une période de transition immense.

Nous pourrions aujourd'hui acheter un ordinateur de bureau à 12 cœurs.
Nous pourrions aujourd'hui acheter une carte de traitement de 448 cœurs (rechercher NVidia Tesla).

Il y a des limites à ce que nous, les développeurs, pouvons travailler dans l'ignorance des environnements extrêmement parallèles dans lesquels nos programmes travailleront dans un avenir proche.

Les systèmes d'exploitation, les environnements d'exécution et les bibliothèques de programmation ne peuvent que faire beaucoup.

À l'avenir, nous devrons partitionner notre traitement en morceaux discrets pour un traitement indépendant, en utilisant des abstractions comme le nouveau "Task Framework" .NET.

Des détails tels que la gestion du cache et l'affinité seront toujours présents - mais ils ne seront que la provence de l'application ultra-performante. Aucun même développeur ne voudra gérer ces détails manuellement sur une machine cœur 10k.

Bevan
la source
3

eh bien, cela dépend vraiment de ce que vous développez. la réponse, selon ce que vous développez, peut aller de "c'est insignifiant" à "c'est absolument essentiel, et nous attendons de tout le monde dans l'équipe une bonne compréhension et utilisation des implémentations parallèles".

dans la plupart des cas, une bonne compréhension et utilisation des verrous, des threads, des tâches et des pools de tâches sera un bon début lorsque le besoin de parallélisme est requis. (varie selon lang / lib)

Ajoutez à cela les différences dans les conceptions que vous devez faire - pour le multitraitement non trivial, il faut souvent apprendre plusieurs nouveaux modèles de programmation ou stratégies de parallélisation. dans ce cas, le temps d'apprendre, d'échouer suffisamment pour avoir une bonne compréhension et de mettre à jour les programmes existants peut prendre une équipe par an (ou plus). une fois que vous avez atteint ce point, vous (espérons-le!) ne percevrez pas ou n'aborderez pas les problèmes / implémentations comme vous le faites aujourd'hui (à condition que vous n'ayez pas encore effectué cette transition).

un autre obstacle est que vous optimisez efficacement un programme pour une certaine exécution. si vous ne disposez pas de beaucoup de temps pour optimiser les programmes, vous n'en bénéficierez pas autant que vous le devriez. la parallélisation de haut niveau (ou évidente) peut améliorer la vitesse perçue de votre programme avec assez peu d'effort, et c'est aussi loin que de nombreuses équipes iront aujourd'hui: "Nous avons parallélisé les parties vraiment évidentes de l'application" - c'est bien dans certains cas. l'avantage de prendre les fruits bas et d'utiliser une simple parallélisation sera-t-il proportionnel au nombre de cœurs? souvent, quand il y a deux à quatre cœurs logiques, mais pas si souvent au-delà. dans de nombreux cas, c'est un rendement acceptable, compte tenu du temps investi. ce modèle parallèle est l'introduction de nombreuses personnes à la mise en œuvre de bonnes utilisations du parallélisme.

ce que vous apprendrez en utilisant ces modèles parallèles triviaux ne sera pas idéal dans tous les scénarios parallèles complexes; appliquer efficacement des conceptions parallèles complexes nécessite une compréhension et une approche très différentes. ces modèles simples sont souvent détachés ou ont une interaction triviale avec d'autres composants du système. de plus, de nombreuses implémentations de ces modèles triviaux ne s'adaptent pas bien à des systèmes parallèles efficacement complexes - une mauvaise conception parallèle complexe peut prendre autant de temps à s'exécuter que le modèle simple. ill: il s'exécute deux fois plus vite que le modèle à un seul thread, tout en utilisant 8 cœurs logiques pendant l'exécution. les exemples les plus courants utilisent / créent trop de threads et des niveaux élevés d'interférence de synchronisation. en général, on parle de ralentissement parallèle. il est assez facile à rencontrer si vous abordez tous les problèmes parallèles comme des problèmes simples.

alors, disons que vous devez vraiment utiliser le multithreading efficace dans vos programmes (la minorité, dans le climat actuel): vous devrez utiliser le modèle simple efficacement pour apprendre le modèle complexe, puis réapprendre comment vous approchez le flux et l'interaction du programme. le modèle complexe est l'endroit où votre programme devrait finalement être, car c'est là que se trouve le matériel aujourd'hui, et où les améliorations les plus dominantes seront apportées.

l'exécution de modèles simples peut être envisagée comme une fourchette, et les modèles complexes fonctionnent comme un écosystème complexe, euh. Je pense que la compréhension de modèles simples, y compris le verrouillage général et le threading devrait être ou sera bientôt attendue des développeurs intermédiaires lorsque le domaine (dans lequel vous développez) l'utilise. comprendre des modèles complexes est encore un peu inhabituel aujourd'hui (dans la plupart des domaines), mais je pense que la demande augmentera assez rapidement. en tant que développeurs, un plus grand nombre de nos programmes devraient prendre en charge ces modèles, et la plupart des utilisateurs sont loin derrière dans la compréhension et la mise en œuvre de ces concepts. étant donné que le nombre de processeurs logiques est l'un des domaines les plus importants de l'amélioration du matériel, la demande de personnes qui comprennent et peuvent mettre en œuvre des systèmes complexes augmentera certainement.

enfin, beaucoup de gens pensent que la solution consiste simplement à "ajouter une parallélisation". il est souvent préférable de rendre la mise en œuvre existante plus rapide. c'est beaucoup plus facile et beaucoup plus simple dans de nombreux cas. de nombreux programmes à l'état sauvage n'ont jamais été optimisés; certaines personnes avaient juste l'impression que la version non optimisée serait éclipsée par le matériel très prochainement. l'amélioration de la conception ou des algos des programmes existants est également une compétence importante si les performances sont importantes - lancer plus de cœurs aux problèmes n'est pas nécessairement la meilleure ou la solution la plus simple.

Lorsque vous ciblez des PC modernes, la plupart d'entre nous qui ont besoin de mettre en œuvre de bons systèmes parallèles n'auront pas besoin d'aller au-delà du multithreading, du verrouillage, des bibliothèques parallèles, de la valeur d'un livre et de beaucoup d'expérience dans l'écriture et les programmes de test (essentiellement, restructurer de manière significative la façon dont vous approche des programmes d’écriture).

Justin
la source
2

Nous le faisons, mais nous écrivons des logiciels de calcul lourds afin de bénéficier directement de plusieurs cœurs.

Parfois, le planificateur déplace beaucoup les threads entre les cœurs. Si ce n'est pas acceptable, vous pouvez jouer avec l'affinité principale.

Toon Krijthe
la source
0

À l'heure actuelle, la fréquence du processeur n'augmentera pas dans un avenir proche. Nous sommes coincés autour de la marque des 3 GHz (sans overclocking). Certes, pour de nombreuses applications, il peut ne pas être nécessaire d'aller au-delà du multi-threading très basique. De toute évidence, si vous créez une application d'interface utilisateur, tout traitement intensif doit être effectué sur un thread d'arrière-plan.

Si vous créez une application qui traite d'énormes quantités de données qui doivent être en temps réel, alors oui, vous devriez probablement vous pencher sur la programmation multithread.

Pour la programmation multi-thread, vous constaterez que vous obtiendrez des rendements décroissants sur vos performances; vous pouvez passer des heures et améliorer le programme de 15%, puis passer une autre semaine et ne l'améliorer que de 5% supplémentaires.

Harry
la source