10
votes

Est mis à jour une double opération atomique

en Java, la mise à jour double et longue variable peut ne pas être atomique, car deux variables séparées de 32 bits séparées.

http: //java.sun. com / docs / livres / jls / second_edition / html / memory.doc.html # 28733

en C ++, si j'utilise le processeur Intel 32 bits + Compilateur Microsoft Visual C ++, met à jour le double (8 octets) opération atomique?

Je ne trouve pas beaucoup de spécifications mentionnées sur ce comportement.

Quand je dis "variable atomique", voici ce que je veux dire:

Fil d'essayer d'écrire 1 à la variable x. Fil b Essayer d'écrire 2 à la variable X.

Nous deviendrons la valeur 1 ou 2 de la variable X, mais pas une valeur non définie.


1 commentaires

Oui, 32 bits x86 (depuis le pentium original) a Prise en charge du matériel efficace pour le verrouillage std :: atomique charge, magasin et CAS. Si votre compilateur rend le code efficace ou non est un autre problème: Stackoverflow.com/Questtions/45055402/... . Aligné double n'aura jamais "déchirant", mais il est plus sûr d'utiliser std :: atomic .


5 Réponses :


-2
votes

Je ne penserais pas dans aucune architecture, la commutation de thread / contexte interrompt la mise à jour d'un registre à mi-chemin de manière à ce que vous restiez par exemple 18bits mis à jour sur les 32bits, il allait mettre à jour. Même pour mettre à jour un emplacement de mémoire (à condition qu'il s'agisse d'une unité d'accès basique, 8,16,32,64 bits, etc.).


3 commentaires

Le problème n'est pas une commutation de contexte, c'est multicœur et MulticPu.


Même dans l'architecture multicore / Multi CPU L'accès à la mémoire physique doit être sérialisé par le contrôleur de mémoire. Il est électriquement impossible de laisser plusieurs périphériques accéder au même circuit à la même durée. Le contrôleur de mémoire accède à la mémoire en blocs et dans les unités entières de largeur de bus de données, il n'est donc pas possible d'avoir une mise à jour partielle d'un emplacement de mémoire.


Alors, qu'en est-il de si le double se trouve à travers la limite de deux lignes de cache? Je doute que MSVC ++ le fera, car tout sera aligné sur sa taille en pouvoirs de 2. Mais si vous généralisez, ce n'est pas une exigence de la norme C ++ (et dans au moins l'un des ARM ABIS, LIPS et DOUBLES seulement doivent être 4-alignés, pas 8-alignés).



11
votes

Ceci est spécifique au matériel et dépend d'une architecture. Pour les écrires X86 et X86_64 8 octets ou des lectures sont garantis en atomique, s'ils sont alignés. Ciblage de la mémoire d'architecture Intel Commander du papier blanc:

Garanties de commande de mémoire Intel 64 que pour chacun des éléments suivants instructions d'accès à la mémoire, le Fonctionnement de la mémoire constituante apparaît Pour exécuter comme un seul accès à la mémoire Quel que soit le type de mémoire:

  1. instructions qui lisent ou écrivent un seul octet.

  2. instructions qui lisent ou écrivent un mot (2 octets) dont l'adresse est aligné sur une limite de 2 octets.

  3. instructions qui lisent ou écrivent un double mot (4 octets) dont l'adresse est aligné sur une limite de 4 octets.

  4. instructions qui lisent ou écrivent un quadwout (8 octets) dont l'adresse est aligné sur une limite de 8 octets.

    toutes les instructions verrouillées (l'implicitement Instruction XCHG verrouillée et autre Instructions de lecture-modifier-écriture avec un le préfixe de verrouillage) sont indivisibles et Séquence ininterrompue de charge (s) suivi de la (s) magasin (s) indépendamment de Type de mémoire et alignement.


4 commentaires

Cela dépend également du compilateur, qui n'est pas nécessaire de s'assurer que les doubles sont 8-alignés en premier lieu ou d'utiliser un seul quaddwwork OP pour les lire ou les écrire. Bien que vous le pensiez probablement, et que je m'attends aussi à ce que Visual C ++ documente si elle le fait ou non.


Oui, cela est spécifié dans les compilateurs ABI. Pour les variables non automatiques, les doubles sont toujours alignées, sauf si elles sont explicitement spécifiées non alignées. Pour les variables sur la pile dans un système 32 bits, ils peuvent devenir non alignés si la pile est d'une manière d'une manière ou d'une autre, par exemple, une fonction est appelée à partir d'un programme externe, non-C. Mais vous ne voulez pas retourner des objets de la pile dans une fonction de toute façon ...


Vous ne retournez pas d'automatics, mais vous risquez de transmettre un pointeur dans une fonction que vous appelez. Mais je suppose que ce que vous avez dit est suffisant pour le questionneur - tant qu'il contrôlait la manière dont ils ont été définis, il peut s'assurer que l'accès à ses doubles est atomique.


Voici la règle pour l'IA-32 (par la question): Le processeur du Pentium (et les transformateurs plus récents depuis) ​​garantit que les opérations de mémoire supplémentaires suivantes seront toujours effectuées atomiquement: • lire ou écrire un quadwwwwwotwwodwwork aligné sur un 64- Limite de bits • Accédez à 16 bits à des emplacements de mémoire incachetés qui correspondent à un autobus de données de 32 bits Les processeurs de la famille P6 (et les nouveaux processeurs depuis) ​​garantissent que l'opération de mémoire supplémentaire suivante sera toujours effectuée atomiquement: • non alinéa 16- 32 - et 64 bits ont accès à la mémoire mise en cache qui correspond à une ligne de cache



-2
votes

Cette question a-t-elle été répondue? J'ai exécuté un programme de test simple changeant un double:

#include <stdio.h>

int main(int argc, char** argv)
{
    double i = 3.14159265358979323;
    i += 84626.433;
}


1 commentaires

"Un fil peut-il vraiment être interrompu dans une seule instruction telle que FADDP?". Non, le thread ne peut pas être "interrompu" à l'intérieur d'une seule instruction, par deux processeurs peut exécuter leurs instructions en même temps, et une CPU ne peut voir que la partie du résultat du deuxième processeur si l'instruction n'est pas effectuée dans un seul. transaction de bus.



2
votes

Il est prudent de supposer que la mise à jour d'un double n'est jamais atomique, même si sa taille est identique à celle d'une assurance atomique. La raison en est que, si elle a un chemin de traitement différent puisqu'il s'agit d'un type de données non critique et coûteux. Par exemple, même des barrières de données mentionnent généralement qu'ils ne s'appliquent pas aux données / opérations de point flottant en général.

Visual C ++ allignera tous les types primitifs (voir article < / a>) et tandis que cela devrait garantir que ses bits ne se soucieront pas tout en écrivant à la mémoire (8 octets allimet étant toujours dans une ligne de cache de 64 ou 128 bits), le reste dépend de la manière dont la CPU gère les données non atomiques dans son cache Et si la lecture / rincer une ligne de cache est interruptible. Donc, si vous creusez à travers Intel Docs pour le type de noyau que vous utilisez et cela vous donne cette garantie que vous êtes en sécurité.

La raison pour laquelle Java Spec est si conservateur est que cela est censé fonctionner de la même manière sur un ancien 386 et sur Corei7. Ce qui est bien sûr délirant mais une promesse est une promesse, elle proma donc moins :-)

La raison pour laquelle je dis que vous devez rechercher le CPU Doc, c'est que votre processeur pourrait être un ancien 386, sinon même :-)) N'oubliez pas que sur un CPU 32 bits, votre bloc de 8 octets prend 2 "rounds" pour accéder afin que vous soyez à la merci de la mécanique de l'accès au cache.

La ligne de cache rinçage donnant une garantie de cohérence de données beaucoup plus élevée ne s'applique qu'à une CPU raisonnablement récente avec garantie Intel-IAN (cohérence automatique de cache).


3 commentaires

Être un processeur X86 de 32 bits ne signifie pas que tous les chemins de données internes ne sont que 32 bits. Par exemple, Pentium 4 (même le plus précoce 32 bits-Seul P4) fait une charge d'alignée 16 octets dans un seul accès à son cache L1D. Vous faites un bon point que l'accès atomique au cache ne garantit pas l'atomicité globalement (par exemple, AMD K10 dispose de charges / magasins Atomic 16B 16B dans une seule prise, mais le protocole de cohérence introduit Déchirant les frontières 8B pour les fils sur différentes prises )


Certains des plus anciens systèmes 386 auparavant n'ont toujours eu qu'un bus de données de 16 bits (et pas de cache interne), de sorte que cela signifierait un double a pris 4 cycles de mémoire. Quoi qu'il en soit, depuis que MSVC ++ moderne ne fera pas de code qui fonctionne même sur tout autre chose qu'un Pentium (P5), vous êtes assuré que les charges / magasins alignés sont atomiques, même si elles sont faites avec X87 ou SSE. (Aucune idée de la raison pour laquelle vous dites FP est moins optimisée. X86 a eu un point flottant hautes performances pendant des années.) Une fois que les données atteignent le cache, il ne se rappelle pas comment il est arrivé là-bas, de sorte que "données non atomiques dans le cache" est bizarre.


C'est Safe supposer que la mise à jour d'un double n'est jamais atomique, mais trop conservatrice.



-2
votes

Sur un multicore, en plus d'être atomique, vous devez vous inquiéter de la cohérence de cache, de sorte que la lecture du fil constate la nouvelle valeur dans son cache lorsque l'auteur a mis à jour.


3 commentaires

L'atomicité et la cohérence de cache sont deux choses différentes. L'atomicité est liée à ce que vous voyiez l'ancienne valeur ou la nouvelle, jamais un état transitoire qui pourrait être nécessaire, la cohérence de cache est liée à la commande dans laquelle vous voyez la modification de plusieurs emplacements de mémoire.


x86 a des caches cohérentes. Vous n'avez pas à vous en soucier. Tant que le lecteur se recharge réellement de la mémoire, au lieu de réutiliser une valeur, le compilateur peut conserver dans un registre, il verra éventuellement la mise à jour. (C'est pourquoi vous devez utiliser std :: atomic maintenant que c ++ 11 existe. Bien que les compilateurs actuels rendent un code inefficace pour celui-ci: Stackoverflow .com / questions / 45055402 / ... )


@Aprogrammer: La cohérence signifie que deux caches ne peuvent avoir différentes valeurs pour une ligne de cache, une fois qu'un magasin s'est engagé dans le cache L1D dans un processeur, aucun autre processeur ne peut charger une valeur différente. en.wikipedia.org/wiki/mesi_protocol . Commander entre modifications à différentes lignes de cache est un autre niveau de fonctionnalité construit sur des caches cohérentes.