11.4.3.2.3 : Abstractions d'exécution parallèle
L'abstraction principale fournie par le système d'exploitation pour permettre le calcul multi-cœur est le thread. Un processus en cours d'exécution peut créer autant de threads qu'il le souhaite, et le système d'exploitation se chargera d'organiser l'exécution de ceux-ci sur les différentes ressources de calcul disponible noteParfois, la logique d'ordonnancement par défaut du système d'exploitation n'est pas optimale. Dans ce cas, le programme peut le guider en lui indiquant la meilleure répartition des threads sur les ressources d'exécution disponibles..
Sous les systèmes POSIX, comme Linux, l'API système principale pour créer et gérer des threads est PThread[66]Threading Building Blocks, Open Group. Comme beaucoup d'APIs POSIX, sa standardisation a malheureusement été un échec partiel, car son comportement varie tellement d'une implémentation à l'autre qu'il n'est pas vraiment possible de l'utiliser pour programmer plusieurs systèmes de façon portable. Par conséquent, lorsqu'une portabilité entre systèmes d'exploitation est désirée, on devra avoir recours à une couche d'abstraction.
Celle-ci est souvent fournie directement par la bibliothèque standard du langage de programmation utilisé, à condition toutefois que celui-ci supporte pleinement les threads. En effet, le support des threads compliquant l'implémentation, tous les langages de programmation ne supportent pas leur utilisation, et ceux qui le supportent ne permettent pas forcément leur exécution simultanée. Ainsi le langage Python par exemple permet de créer plusieurs tâches, mais pas de les exécuter simultanément.
Lorsque c'est le cas, une technique de contournement consiste à lancer $n$ copies du programme, chacune tournant dans son propre processus système. Mais cette technique possède un grand nombre d'inconvénients~:
- Le processus étant l'entité utilisée par le système d'exploitation pour cloisonner les ressources, il est très difficile de partager des ressources système (mémoire, fichiers, connexions réseau... ) entre processus.
- Il est donc difficile de ne pas consommer $n$ fois plus de RAM en dupliquant toutes les ressources partagées de l'application, problème qui n'existe pas en multi-thread.
- Les primitives fournies par le système d'exploitation pour la communication inter-processus sont souvent bien plus complexes et/ou bien moins performantes que celles utilisables pour la communication entre threads.
- Le processus étant l'entité atomique du crash, un programme multi-processus doit gérer à chaque instant la possibilité que l'un de ses sous-processus rencontre un problème, alors qu'en multi-thread le crash d'un thread emmène automatiquement le reste de l'application avec lui noteCette propriété des conceptions multi-processus peut aussi être avantageusement utilisée pour augmenter la robustesse d'un programme face aux erreurs. Cependant, l'écriture de programmes robustes est très difficile, et les codes de calcul n'ont généralement pas besoin de cette garantie..
Quand on met tout cela bout à bout, la programmation d'un calcul multi-processus devient souvent aussi complexe que celle d'un calcul distribué, avec les mêmes difficultés de passage à l'échelle, et rend très difficile l'exploitation efficace de la mémoire partagée qui est l'élément central du parallélisme multi-cœur.
Par conséquent, les langages de programmation ne permettant pas l'utilisation de threads pour le parallélisme doivent être évités autant que faire se peut, et quand on n'a pas le choix ils doivent être interfacés à d'autres langages qui n'ont pas cette limitation pour tirer au mieux parti du parallélisme intra-nœud.