10
votes

Comparer et échanger dans le code de la machine en C

Comment écririez-vous une fonction en C qui fait un comparateur atomique et échange de valeur entière, en utilisant du code de machine incorporé (supposant, disons, architecture X86)? Peut-il être plus spécifique si son écrit uniquement pour le processeur I7?

La traduction agira-t-elle comme une clôture de mémoire ou garantit-elle simplement une relation de commande juste sur cet emplacement de mémoire inclus dans le comparateur et l'échange? À quel point est-il coûteux par rapport à une clôture de mémoire?

merci.


2 commentaires

Habituellement, vous comparez et échangez une valeur avec un emplacement de mémoire, vous semblez parler de Deux emplacements de mémoire plus complexes. Est-ce certainement ce dont vous avez besoin?


Désolé, je voulais dire un emplacement de mémoire unique, je vais clarifier.


5 Réponses :


7
votes

Le moyen le plus simple de le faire est probablement avec un COMPILER intrinsèque comme _InterLockedCompeExchange () . Cela ressemble à une fonction mais est en fait un cas particulier dans le compilateur qui se résume à une seule machine OP. Dans le cas de l'intrinsèque MSVC X86, cela fonctionne comme une clôture de lecture / écriture également, mais ce n'est pas nécessairement vrai sur d'autres plates-formes. (Par exemple, sur le PowerPC, vous devez explicitement émettre un LWSYNC pour clôturer.)

En général, sur de nombreux systèmes communs, une opération de comparaison-et-échange n'applique généralement qu'une transaction atomique sur une adresse d'une seule adresse qui touche. L'autre accès à la mémoire peut être réorganisé et dans des systèmes multicœurs, des adresses de mémoire autres que celle que vous avez échangées ne sont pas cohérentes entre les cœurs.


7 commentaires

Merci pour votre réponse! Mais est-il même possible de faire une comparaison non-mémoire-clôture sur l'un des systèmes multicœurs actuels? Dans cette question Stackoverflow.com/ Questions / 4183202 / ... , un utilisateur a affirmé que sur les architectures x86, la seule instruction de comparaison-et-swap est CMPXCHG et qu'elle doit protégée via une serrure, qui agit comme une clôture de mémoire, pour le rendre atomique - que c'est le seul moyen. Savez-vous peut-être si cette revendication est correcte?


Je pense que pour le X86, il a raison (je ne suis pas un expert sur Intel). Mais il y a d'autres processeurs avec d'autres sémantiques. Par exemple, le PowerPC a un modèle différent dans lequel elle crée une "réservation" sur une adresse, puis des magasins conditionnellement. Mais cela ne garantit qu'une clôture sur cet endroit. Un magasin précédent vers un emplacement différent d'un autre noyau peut sembler se produire après la comparaison et le swap. De plus, sur cette puce, "la cohérence ne garantit pas que le résultat d'un magasin d'un processeur est immédiatement visible à tous les autres processeurs".


C'est-à-dire: Disons qu'il y a deux noyaux, A et B. Si A, puis B les deux C & S sur Adresse 0x100, ils conviendront de l'ordre dans lequel cela se produit. B verra la valeur d'une première A et ensuite B. Mais si A est une écriture ordinaire de "0" pour adresser 0x100, alors b écrit "1" à 0x100, puis ils ont tous les deux l'adresse 0x200 - ils verront ensuite la même valeur à 0x200, mais une puissance pourrait encore Pensez que 0x100 contient "0". En fait, l'écriture d'A peut arriver à 0x100 après B's, de sorte que la valeur finit vraiment d'être 0.


Merci beaucoup pour cette clarification! C'est ce que je voulais savoir.


Je ne trouve pas que votre deuxième paragraphe soit entièrement correct, ou peut-être que ce n'est pas clair. Généralement, une opération de "verrouillage" / applique / applique une sorte de contrainte de commande, sinon il y aurait peu de valeur pour l'utiliser. De plus, alors que la mémoire réelle peut ne pas être identique, la vue sur une autre mémoire est toujours cohérente dans un système CCNUMA.


@ EDA-QA Mort-ora-y: Pour des exemples, jetez un coup d'œil à www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/... et www-01.ibm.com/chips/techlib/techlib.nsf/products/... .


Ensuite, contrairement à Intel I7, qui est CCnuma, PowerPC n'est pas un système CCNUMA. Du lien ci-dessus: "L'architecture PowerPC prévoit une cohérence de la mémoire détendue." et "l'ordre dans lequel le processeur effectue des accès mémoire, l'ordre dans lequel ces accès sont terminés en mémoire et l'ordre dans lequel ces accès sont considérés comme survenus par un autre processeur peut être différent."



7
votes

Vous pouvez utiliser l'instruction CMPXCHG avec le verrouillage préfixe pour l'exécution atomique.

par exemple xxx

ou xxx

ceci compare la valeur dans le registre EAX avec la valeur de l'adresse enregistrée dans le registre EBX et stocke la valeur dans le registre EDX à cet endroit si elles sont les Même, sinon, il charge la valeur à l'adresse stockée dans le registre EBX dans EAX.

Vous devez avoir une 486 ou une version ultérieure pour que cette instruction soit disponible.


3 commentaires

Charles, peut-il être utilisé sans la serrure si on peut garantir que seul un seul thread l'utilise?


@Computer Guru: Oui, il peut être utilisé sans verrouillage .


Merci de confirmer que :)



4
votes

Si votre valeur entière est de 64 bits que d'utiliser CMPXCHG8B 8 octets comparer et échanger sous IA32 X86. La variable doit être alignée de 8 octets.

Example:
      mov   eax, OldDataA           //load Old first 32 bits
      mov   edx, OldDataB           //load Old second 32 bits
      mov   ebx, NewDataA           //load first 32 bits
      mov   ecx, NewDataB           //load second 32 bits
      mov   edi, Destination        //load destination pointer
      lock cmpxchg8b qword ptr [edi]
      setz  al                      //if transfer is succesful the al is 1 else 0


1 commentaires

Vous ne chargez jamais EDX et EAX qui est la paire de valeur comparée.



4
votes

Si le préfixe de verrouillage est omis dans les instructions du processeur atomique, l'opération atomique à travers l'environnement multiprocesseur sera ne pas être garantie .

Dans un environnement multiprocesseur, le signal de verrouillage garantit que le processeur a une utilisation exclusive de toute mémoire partagée, tandis que le signal est affirmé. INTEL INSTALLATION INSTALLATION Référence < / p>

sans préfixe de verrouillage, l'opération garantira non interrompue par aucun événement (interruption) sur le processeur / noyau actuel uniquement.


0 commentaires

2
votes

Il est intéressant de noter que certains processeurs ne fournissent pas d'échange de comparaison, mais offrent plutôt d'autres instructions ("chargée de chargement" et "magasin conditionnel") qui peut être utilisée pour synthétiser malheureusement la comparaison - et- Swap (le nom semble être similaire à celui-ci devrait être similaire à "Comparer-échange" mais devrait vraiment être appelé "Comparer-Store" car il effectue la comparaison, stocke si la valeur correspond à la valeur correspond à la valeur correspondante et que le magasin était effectué). Les instructions ne peuvent pas synthétiser la sémantique de comparaison-Exchange (qui fournit la valeur lue au cas où le comparateur a échoué), mais peut dans certains cas, évitez le problème ABA qui est présent avec la comparaison-échange. De nombreux algorithmes sont décrits en termes d'opérations "CAS" car elles peuvent être utilisées sur les deux styles de CPU.

Une instruction "Load Linked" indique au processeur de lire un emplacement de mémoire et de veiller en quelque sorte à voir si cela pourrait être écrit. Une instruction "Store conditionnelle" indique au processeur d'écrire un emplacement de mémoire uniquement si rien ne peut l'avoir écrit depuis la dernière opération "chargée liée". Notez que la détermination peut être pessimiste; Traitement d'une interruption, par exemple, peut invalider une séquence "chargée de stockage" / "conditionnelle". De même dans un système multi-processeurs, une séquence LL / CS peut être invalidée par un autre processeur accédant à un emplacement sur la même ligne de cache que l'emplacement surveillé, même si l'emplacement réel observé n'a pas été touché. Dans une utilisation typique, les ll / cs sont utilisés très proches ensemble, avec une boucle de réessaye, de sorte que les invalidations erronées puissent ralentir des choses un peu mais ne causeront pas de problèmes.


0 commentaires