web-dev-qa-db-fra.com

Est-il judicieux d'utiliser l'instruction LFENCE sur les processeurs x86 / x86_64?

Souvent, sur Internet, je trouve que LFENCE n'a aucun sens dans les processeurs x86, c'est-à-dire qu'il ne fait rien, donc à la place MFENCE nous pouvons absolument indolore utiliser SFENCE, parce que MFENCE = SFENCE + LFENCE = SFENCE + NOP = SFENCE.

Mais si LFENCE n'a pas de sens, alors pourquoi nous avons quatre approches pour rendre la cohérence séquentielle dans x86/x86_64:

  1. LOAD (sans clôture) et STORE + MFENCE
  2. LOAD (sans clôture) et LOCK XCHG
  3. MFENCE + LOAD et STORE (sans clôture)
  4. LOCK XADD (0) et STORE (sans clôture)

Extrait d'ici: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

Ainsi que les performances de Herb Sutter à la page 34 en bas: https://skydrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&wdo=2&authkey=!AMtj_EflYn2507c

Si LFENCE n'a rien fait, alors l'approche (3) aurait les significations suivantes: SFENCE + LOAD and STORE (without fence), mais il est inutile de faire SFENCE avant LOAD. Autrement dit, si LFENCE ne fait rien, l'approche (3) n'a pas de sens.

Cela a-t-il un sens pour les instructions LFENCE dans les processeurs x86/x86_64?

RÉPONSE:

1. LFENCE requis dans les cas décrits dans la réponse acceptée ci-dessous.

2. L'approche (3) doit être vue non pas indépendamment, mais en combinaison avec les commandes précédentes. Par exemple, approche (3):

MFENCE
MOV reg, [addr1]  // LOAD-1
MOV [addr2], reg  //STORE-1

MFENCE
MOV reg, [addr1]  // LOAD-2
MOV [addr2], reg  //STORE-2

On peut réécrire le code d'approche (3) comme suit:

SFENCE
MOV reg, [addr1]  // LOAD-1
MOV [addr2], reg  //STORE-1

SFENCE
MOV reg, [addr1]  // LOAD-2
MOV [addr2], reg  //STORE-2

Et ici, SFENCE est logique pour éviter de réorganiser STORE-1 et LOAD-2. Pour cela, après la commande STORE-1 SFENCE vide la mémoire tampon du magasin.

43
Alex

Conclusion (TL; DR): LFENCE seul semble en effet inutile pour l'ordre de la mémoire, mais cela ne fait pas de SFENCE un substitut de MFENCE. La logique "arithmétique" de la question n'est pas applicable.


Voici un extrait de Intel's Software Developers Manual, volume , section 8.2.2 (l'édition 325384-052US de septembre 2014), le même que j'ai utilisé dans ne autre réponse

  • Les lectures ne sont pas réorganisées avec d'autres lectures.
  • Les écritures ne sont pas réorganisées avec des lectures plus anciennes.
  • Les écritures en mémoire ne sont pas réorganisées avec d'autres écritures, avec les exceptions suivantes:
    • écrit exécuté avec l'instruction CLFLUSH;
    • les magasins de streaming (écritures) exécutés avec les instructions de déplacement non temporelles (MOVNTI, MOVNTQ, MOVNTDQ, MOVNTPS et MOVNTPD); et
    • opérations de chaîne (voir Section 8.2.4.1).
  • Les lectures peuvent être réorganisées avec des écritures plus anciennes vers des emplacements différents mais pas avec des écritures plus anciennes vers le même emplacement.
  • Les lectures ou écritures ne peuvent pas être réorganisées avec des instructions d'E/S, des instructions verrouillées ou des instructions de sérialisation.
  • Les lectures ne peuvent pas passer les instructions LFENCE et MFENCE antérieures.
  • Les écritures ne peuvent pas passer les instructions LFENCE, SFENCE et MFENCE antérieures.
  • Les instructions LFENCE ne peuvent pas passer des lectures antérieures.
  • Les instructions SFENCE ne peuvent pas passer des écritures antérieures.
  • Les instructions MFENCE ne peuvent pas passer des lectures ou des écritures antérieures.

D'ici, il s'ensuit que:

  • MFENCE est une clôture de mémoire complète pour toutes les opérations sur tous les types de mémoire, qu'elles soient non temporelles ou non.
  • SFENCE empêche uniquement la réorganisation des écritures (dans une autre terminologie, il s'agit d'une barrière StoreStore) et n'est utile qu'avec les magasins non temporels et d'autres instructions répertoriées comme exceptions.
  • LFENCE empêche la réorganisation des lectures avec les lectures et écritures suivantes (c'est-à-dire qu'il combine les barrières LoadLoad et LoadStore). Cependant, les deux premières puces indiquent que les barrières LoadLoad et LoadStore sont toujours en place, sans exception. Par conséquent, LFENCE seul est inutile pour l'ordre de la mémoire.

Pour soutenir la dernière affirmation, j'ai examiné tous les endroits où LFENCE est mentionné dans les 3 volumes du manuel d'Intel, et n'en ai trouvé aucun qui dirait que LFENCE est requis pour la cohérence de la mémoire. Même MOVNTDQA - la seule instruction de chargement non temporel jusqu'à présent - mentionne MFENCE mais pas LFENCE.


Mise à jour: voir les réponses sur Pourquoi (ou non?) SFENCE + LFENCE est-il équivalent à MFENCE? pour les réponses correctes aux hypothèses ci-dessous

La question de savoir si MFENCE équivaut ou non à une "somme" d'autres deux clôtures est une question délicate. À première vue, parmi les trois instructions de clôture, seul MFENCE fournit la barrière StoreLoad, c'est-à-dire empêche la réorganisation des lectures avec les écritures antérieures. Cependant, la bonne réponse nécessite d'en savoir plus que les règles ci-dessus; à savoir, il est important que toutes les instructions de clôture soient ordonnées les unes par rapport aux autres. Cela rend le SFENCE LFENCE séquence plus puissante qu'une simple union d'effets individuels: cette séquence empêche également la réorganisation de StoreLoad (car les charges ne peuvent pas passer LFENCE, qui ne peut pas passer SFENCE, qui ne peut pas passer de magasins), et constitue ainsi une clôture pleine mémoire (mais aussi voir la note (*) ci-dessous). Notez cependant que l'ordre est important ici et que le LFENCE SFENCE la séquence n'a pas le même effet de synergie.

Cependant, alors que l'on peut dire que MFENCE ~ SFENCE LFENCE et LFENCE ~ NOP, cela ne veut pas dire MFENCE ~ SFENCE. J'utilise délibérément l'équivalence (~) et non l'égalité (=) pour souligner que les règles arithmétiques ne s'appliquent pas ici. L'effet mutuel de SFENCE suivi de LFENCE fait la différence; même si les charges ne sont pas réorganisées les unes avec les autres, LFENCE est requis pour empêcher la réorganisation des charges avec SFENCE.

(*) Il serait toujours correct de dire que MFENCE est plus fort que la combinaison des deux autres clôtures. En particulier, une note relative aux instructions CLFLUSH dans le volume 2 du manuel d'Intel indique que "CLFLUSH n'est commandé que par l'instruction MFENCE. Il n'est pas garanti qu'il soit commandé par toute autre instruction de clôture ou de sérialisation ou par une autre instruction CLFLUSH. "

(Update, clflush est maintenant défini comme fortement ordonné (comme un magasin normal, donc vous n'avez besoin de mfence que si vous voulez bloquer plus tard charges), mais clflushopt est faiblement ordonné, mais peut être clôturé par sfence.)

31
Alexey Kukanov

Considérez le scénario suivant - c'est le cas critique où l'exécution de charge spéculative peut théoriquement nuire à la cohérence séquentielle

initialement [x] y] = 0

CPU0:                              CPU1: 
store [x]<--1                      store [y]<--1
load  r1<--[y]                     load r2<--[x]

Étant donné que x86 permet de réorganiser les charges avec des magasins antérieurs à différentes adresses, les deux charges peuvent renvoyer des 0. L'ajout d'un lfence seul après chaque magasin n'empêchera pas cela, car ils n'empêchent que le réordonnancement dans le même contexte, mais comme les magasins sont distribués après la retraite, vous pouvez avoir les deux lfences et les deux charges validées avant que les magasins soient exécutés et observés.

Un mfence, d'autre part, forcerait les magasins à fonctionner et ne permettrait alors que les chargements à exécuter, vous verrez donc les données mises à jour sur au moins un contexte.

Quant à sfences - comme indiqué dans le commentaire, en théorie, il n'est pas assez fort pour empêcher la charge de se réorganiser au-dessus, il peut donc toujours lire des données périmées. Bien que cela soit vrai en ce qui concerne les règles de commande officielles de la mémoire, je pense que l'implémentation actuelle de x86 uarch le rend légèrement plus fort (sans m'engager à le faire à l'avenir, je suppose). Selon cette description :

En raison du fort modèle de classement x86, le tampon de chargement est espionné par le trafic de cohérence. Un magasin distant doit invalider toutes les autres copies d'une ligne de cache. Si une ligne de cache est lue par une charge, puis invalidée par un magasin distant, la charge doit être annulée, car elle peut potentiellement lire des données non valides. Le modèle de mémoire x86 ne nécessite pas d'espionner le tampon de stockage.

Par conséquent, toute charge non encore validée dans la machine doit être récupérable par les magasins d'autres cœurs, ce qui rend le temps d'observation effectif de la charge au point commit, et non le == [- exécution point (qui est en effet hors service et peut avoir été exécuté beaucoup plus tôt). La validation est effectuée dans l'ordre, et donc la charge doit être observée après les instructions précédentes - ce qui rend les lfences à peu près inutiles comme je l'ai dit ci-dessus dans les commentaires, car la cohérence peut être maintenue de la même manière sans eux. Il s'agit principalement de spéculations, essayant d'expliquer la conception commune selon laquelle les lfences n'ont pas de sens dans x86 - je ne suis pas tout à fait sûr de l'origine et s'il y a d'autres considérations à portée de main - serait heureux pour tout expert d'approuver/de contester cette théorie.

Tout ce qui précède ne s'applique bien sûr qu'aux types de mem WB

8
Leeor