273 votes

À quoi ressemble le langage d'assemblage multicore ?

Il fut un temps où, pour écrire l'assembleur x86, par exemple, vous aviez des instructions disant "charger le registre EDX avec la valeur 5", "incrémenter le registre EDX", etc.

Avec les processeurs modernes qui ont 4 cœurs (ou même plus), au niveau du code machine, est-ce qu'on a l'impression qu'il y a 4 processeurs séparés (c'est-à-dire qu'il y a juste 4 registres "EDX" distincts) ? Si c'est le cas, lorsque vous dites "incrémenter le registre EDX", qu'est-ce qui détermine quel registre EDX du CPU est incrémenté ? Y a-t-il un concept de "contexte CPU" ou de "thread" dans l'assembleur x86 maintenant ?

Comment fonctionne la communication/synchronisation entre les cœurs ?

Si vous écriviez un système d'exploitation, quel mécanisme est exposé via le matériel pour vous permettre de planifier l'exécution sur différents cœurs ? S'agit-il d'une ou de plusieurs instructions spéciales privilégiées ?

Si vous deviez écrire un compilateur optimisant/une VM de bytecode pour un processeur multicœur, que devriez-vous savoir spécifiquement sur, disons, x86 pour qu'il génère un code qui s'exécute efficacement sur tous les cœurs ?

Quelles modifications ont été apportées au code machine x86 pour prendre en charge la fonctionnalité multi-cœur ?

2 votes

Une question similaire (mais pas identique) se pose ici : stackoverflow.com/questions/714905/

181voto

Nathan Fellman Points 31310

Ce n'est pas une réponse directe à la question, mais c'est une réponse à une question qui apparaît dans les commentaires. Essentiellement, la question est de savoir quel support le matériel offre au fonctionnement multithread.

Nicholas Flynt avait raison du moins en ce qui concerne x86. Dans un environnement multi-filière (Hyper-threading, multi-cœur ou multi-processeur), les Fil conducteur de Bootstrap (généralement le thread 0 du noyau 0 du processeur 0) commence à récupérer le code à l'adresse 0xfffffff0 . Tous les autres threads démarrent dans un état de sommeil spécial appelé Wait-for-SIPI . Dans le cadre de son initialisation, le thread primaire envoie une interruption interprocesseur (IPI) spéciale sur l'APIC, appelée SIPI (Startup IPI), à chaque thread qui se trouve dans WFS. Le SIPI contient l'adresse à partir de laquelle ce thread doit commencer à récupérer du code.

Ce mécanisme permet à chaque thread d'exécuter du code à partir d'une adresse différente. Il suffit que le logiciel permette à chaque thread de créer ses propres tables et files d'attente de messagerie. Le système d'exploitation utilise ceux pour effectuer l'ordonnancement multithread réel.

En ce qui concerne l'assemblage proprement dit, comme l'a écrit Nicholas, il n'y a pas de différence entre les assemblages pour une application à filetage unique ou à filetage multiple. Chaque thread logique a son propre jeu de registres, donc l'écriture :

mov edx, 0

ne mettra à jour que EDX pour le Fil conducteur actuel . Il n'y a aucun moyen de modifier EDX sur un autre processeur en utilisant une seule instruction d'assemblage. Vous avez besoin d'une sorte d'appel système pour demander au système d'exploitation de dire à un autre thread d'exécuter un code qui mettra à jour ses propres données. EDX .

2 votes

Merci de combler le vide dans la réponse de Nicholas. J'ai marqué la vôtre comme étant la réponse acceptée maintenant.... donne les détails spécifiques qui m'intéressaient... bien que ce serait mieux s'il y avait une seule réponse qui réunissait vos informations et celles de Nicholas.

4 votes

Cela ne répond pas à la question de savoir d'où viennent les fils. Les cœurs et les processeurs sont des éléments matériels, mais d'une manière ou d'une autre, les threads doivent être créés dans le logiciel. Comment le thread primaire sait-il où envoyer le SIPI ? Ou est-ce que le SIPI lui-même crée un nouveau thread ?

10 votes

@richremer : Il semble que vous confondiez les fils HW et les fils SW. Le fil HW existe toujours. Parfois, il est endormi. Le SIPI lui-même réveille le HW thread et lui permet d'exécuter le SW. C'est au système d'exploitation et au BIOS de décider quels HW threads s'exécutent, et quels processus et SW threads s'exécutent sur chaque HW thread.

46voto

Nicholas Flynt Points 2832

Si je comprends bien, chaque "cœur" est un processeur complet, avec son propre jeu de registres. En gros, le BIOS vous fait démarrer avec un cœur en marche, puis le système d'exploitation peut "démarrer" d'autres cœurs en les initialisant et en leur indiquant le code à exécuter, etc.

La synchronisation est effectuée par le système d'exploitation. En général, chaque processeur exécute un processus différent pour le système d'exploitation, de sorte que la fonctionnalité multithreading du système d'exploitation est chargée de décider quel processus peut toucher quelle mémoire, et ce qu'il faut faire en cas de collision de mémoire.

31 votes

Ce qui pose la question : Quelles sont les instructions dont dispose le système d'exploitation pour faire cela ?

4 votes

Il existe un ensemble d'instructions privilégiées pour cela, mais c'est le problème du système d'exploitation, pas du code d'application. Si le code d'application veut être multithreadé, il doit appeler les fonctions du système d'exploitation pour faire la "magie".

2 votes

Le BIOS identifie généralement le nombre de cœurs disponibles et transmet cette information au système d'exploitation lorsqu'il le lui demande. Il existe des normes auxquelles le BIOS (et le matériel) doit se conformer afin que l'accès aux spécificités du matériel (processeurs, cœurs, bus PCI, cartes PCI, souris, clavier, graphiques, ISA, PCI-E/X, mémoire, etc.) pour différents PC soit identique du point de vue du système d'exploitation. Si le BIOS ne signale pas qu'il y a quatre cœurs, le système d'exploitation suppose généralement qu'il n'y en a qu'un seul. Il peut même y avoir un paramètre du BIOS à expérimenter.

41voto

DigitalRoss Points 80400

La FAQ non officielle du SMP stack overflow logo


Autrefois, pour écrire de l'assembleur x86, par exemple, vous aviez des instructions disant "charger le registre EDX avec la valeur 5", "incrémenter le registre EDX", etc. Avec les processeurs modernes qui ont 4 cœurs (ou même plus), au niveau du code machine, est-ce qu'on a l'impression qu'il y a 4 processeurs séparés (c'est-à-dire qu'il y a seulement 4 registres "EDX" distincts) ?

Exactement. Il y a 4 jeux de registres, y compris 4 pointeurs d'instruction séparés.

Si c'est le cas, lorsque vous dites "incrémenter le registre EDX", qu'est-ce qui détermine quel registre EDX du CPU est incrémenté ?

L'unité centrale qui a exécuté cette instruction, naturellement. Considérez cela comme 4 microprocesseurs entièrement différents qui partagent simplement la même mémoire.

Y a-t-il un concept de "contexte CPU" ou de "thread" dans l'assembleur x86 maintenant ?

Non. L'assembleur traduit simplement les instructions comme il l'a toujours fait. Il n'y a aucun changement.

Comment fonctionne la communication/synchronisation entre les cœurs ?

Comme ils partagent la même mémoire, c'est surtout une question de logique de programme. Bien qu'il existe maintenant un interruption interprocesseur Ce mécanisme n'est pas nécessaire et n'était pas présent à l'origine dans les premiers systèmes x86 à double processeur.

Si vous écriviez un système d'exploitation, quel mécanisme est exposé via le matériel pour vous permettre de planifier l'exécution sur différents cœurs ?

En fait, l'ordonnanceur ne change pas, si ce n'est qu'il est légèrement plus attentif aux sections critiques et aux types de verrous utilisés. Avant le SMP, le code du noyau appelait éventuellement l'ordonnanceur, qui regardait la file d'attente d'exécution et choisissait un processus à exécuter comme prochain thread. (Pour le noyau, les processus ressemblent beaucoup aux threads). Le noyau SMP exécute exactement le même code, un thread à la fois, mais le verrouillage des sections critiques doit maintenant être sécurisé par le SMP pour éviter que deux cœurs ne choisissent accidentellement le même PID.

S'agit-il d'une ou de plusieurs instructions spéciales privilégiées ?

Non. Les cœurs fonctionnent tous dans la même mémoire avec les mêmes anciennes instructions.

Si vous deviez écrire un compilateur optimisant/une VM de bytecode pour un processeur multicœur, que devriez-vous savoir spécifiquement sur, disons, x86 pour qu'il génère un code qui s'exécute efficacement sur tous les cœurs ?

Vous exécutez le même code qu'avant. C'est le noyau d'Unix ou de Windows qui a dû changer.

Vous pourriez résumer ma question comme suit : "Quelles modifications ont été apportées au code machine x86 pour prendre en charge la fonctionnalité multicœur ?"

Rien n'était nécessaire. Les premiers systèmes SMP utilisaient exactement le même jeu d'instructions que les uniprocesseurs. L'architecture x86 a beaucoup évolué et des millions de nouvelles instructions ont été ajoutées pour accélérer les choses, mais aucune n'était nécessaire. necesario pour le SMP.

Pour plus d'informations, voir le Spécification du multiprocesseur Intel .


Mise à jour : toutes les questions de suivi peuvent être répondues en acceptant complètement qu'une n -Un processeur multicore est presque 1 exactement la même chose que n des processeurs séparés qui partagent simplement la même mémoire. 2 Il y a une question importante qui n'a pas été posée : comment écrire un programme pour qu'il fonctionne sur plus d'un cœur pour plus de performances ? Et la réponse est : il est écrit en utilisant une bibliothèque de threads comme Pthreads. Certaines bibliothèques de threads utilisent des "threads verts" qui ne sont pas visibles pour le système d'exploitation, et ceux-ci n'auront pas de cœurs séparés, mais tant que la bibliothèque de threads utilise les fonctionnalités de threads du noyau, votre programme threadé sera automatiquement multicœur.

1. Pour des raisons de rétrocompatibilité, seul le premier noyau démarre à la réinitialisation, et quelques opérations de type pilote doivent être effectuées pour activer les autres noyaux.
2. Ils partagent également tous les périphériques, naturellement.

4 votes

J'ai toujours pensé que le "thread" est un concept logiciel, ce qui me rend difficile la compréhension des processeurs multi-core, le problème est que, comment les codes peuvent-ils dire à un core "Je vais créer un thread fonctionnant dans le core 2" ? Existe-t-il un code d'assemblage spécial pour le faire ?

3 votes

@demonguy : Non, il n'y a pas d'instruction spéciale pour quelque chose comme ça. Vous demandez au système d'exploitation d'exécuter votre thread sur un cœur spécifique en définissant un masque d'affinité (qui dit "ce thread peut fonctionner sur cet ensemble de cœurs logiques"). Il s'agit d'une question purement logicielle. Chaque cœur de processeur (thread matériel) exécute indépendamment Linux (ou Windows). Pour travailler ensemble avec les autres threads matériels, ils utilisent des structures de données partagées. Mais vous ne démarrez jamais "directement" un thread sur un autre CPU. Vous dites au système d'exploitation (OS) que vous souhaitez avoir un nouveau thread, et il en prend note dans une structure de données que le système d'exploitation d'un autre cœur voit.

2 votes

Je peux le dire, mais comment mettre les codes sur un noyau spécifique ?

10voto

Alex Brown Points 15776

Si vous écriviez un programme d'optimisation compilateur/bytecode VM pour un processeur multicore multicore, qu'auriez-vous besoin de savoir spécifiquement sur, disons, x86 pour que qu'il génère du code qui s'exécute efficacement sur tous les cœurs ?

En tant que personne qui écrit des compilateurs optimisants/bytecode VMs, je peux peut-être vous aider ici.

Il n'est pas nécessaire de connaître spécifiquement le système x86 pour que celui-ci génère un code qui s'exécute efficacement sur tous les cœurs.

Cependant, vous pouvez avoir besoin de connaître cmpxchg et ses amis afin d'écrire du code qui fonctionne correctement sur tous les cœurs. La programmation multicore nécessite l'utilisation de la synchronisation et de la communication entre les fils d'exécution.

Il est possible que vous deviez avoir des connaissances sur les x86 pour qu'il génère du code qui fonctionne efficacement sur les x86 en général.

Il y a d'autres choses qu'il vous serait utile d'apprendre :

Vous devez vous renseigner sur les possibilités offertes par le système d'exploitation (Linux, Windows ou OSX) pour vous permettre d'exécuter plusieurs threads. Vous devez vous familiariser avec les API de parallélisation telles qu'OpenMP et Threading Building Blocks, ou le futur "Grand Central" d'OSX 10.6 "Snow Leopard".

Vous devez vous demander si votre compilateur doit s'auto-paralléliser, ou si l'auteur des applications compilées par votre compilateur doit ajouter une syntaxe spéciale ou des appels API dans son programme pour tirer parti des cœurs multiples.

0 votes

Plusieurs VM populaires comme .NET et Java n'ont-elles pas pour problème que leur processus GC principal est couvert de verrous et fondamentalement monofilaire ?

9voto

Gerhard Points 3383

Chaque noyau s'exécute à partir d'une zone de mémoire différente. Votre système d'exploitation dirigera un noyau vers votre programme et le noyau exécutera votre programme. Votre programme ne saura pas qu'il y a plus d'un noyau ni sur quel noyau il s'exécute.

Il n'y a pas non plus d'instruction supplémentaire uniquement disponible pour le système d'exploitation. Ces cœurs sont identiques aux puces à cœur unique. Chaque noyau exécute une partie du système d'exploitation qui se chargera de la communication avec les zones de mémoire communes utilisées pour l'échange d'informations afin de trouver la prochaine zone de mémoire à exécuter.

Il s'agit d'une simplification, mais cela vous donne une idée de base de la façon dont cela se passe. En savoir plus sur les multicores et les multiprocesseurs sur Embedded.com a beaucoup d'informations sur ce sujet ... Ce sujet devient très vite compliqué !

0 votes

Je pense que l'on devrait distinguer un peu plus soigneusement ici comment le multicore fonctionne en général, et dans quelle mesure l'OS influence. "Chaque cœur s'exécute à partir d'une zone de mémoire différente" est trop trompeur à mon avis. Tout d'abord, l'utilisation de plusieurs cœurs en principe ne nécessite pas cela, et vous pouvez facilement voir que pour un programme threadé, vous voudriez que deux cœurs travaillent sur les mêmes segments de texte et de données (alors que chaque cœur a également besoin de ressources individuelles comme la pile).

0 votes

@ShiDoiSi C'est pourquoi ma réponse contient le texte suivant "C'est une simplification" .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X