Question

Le Linux de les utilisations lock; addl $0,0(%%esp) comme barrière d'écriture, tandis que les utilisations bibliothèque RE2 xchgl (%0),%0 comme barrière d'écriture. Quelle est la différence et ce qui est mieux?

ne x86 nécessitent également des instructions de lecture barrière? RE2 définit sa lecture fonction de barrière comme no-op sur x86 alors que Linux définit soit comme lfence ou non-op selon que SSE2 est disponible. Quand est-lfence nécessaire?

Était-ce utile?

La solution

Le " verrouillage; addl $ 0,0 (%% esp) " est plus rapide dans le cas où nous tester l'état 0 de la variable de verrouillage à (%% esp) adresse. Parce que nous ajoutons 0 valeur à la variable de verrouillage et le drapeau zéro est fixé à 1 si la valeur de blocage de la variable à l'adresse (%% esp) est 0.


lfence Intel Fiche technique:

  

Permet de réaliser une opération sur la sérialisation   toutes les instructions de charge-mémoire qui   ont été émis avant l'LFENCE   instruction. cette sérialisation   garanties de fonctionnement que chaque charge   instruction qui précède dans le programme   commander l'instruction est LFENCE   globalement visible avant toute charge   instruction qui suit la LFENCE   instruction est globalement visible.

( Note de l'éditeur: mfence ou une opération locked est la seule clôture utile (après un magasin) pour la cohérence séquentielle . lfence ne pas bloc StoreLoad réordonnancement par le tampon de stockage).


Par exemple: la mémoire instruction d'écriture comme « mov » sont atomiques (ils préfixe de verrouillage ont pas besoin) s'il sont correctement alignés. Mais cette instruction est normalement exécuté dans le cache du processeur et ne sera pas globalement visible à ce moment pour tous les autres fils, parce que la clôture de la mémoire doit être réalisée d'abord pour faire de ce fil d'attente jusqu'à ce que les magasins précédents sont visibles à d'autres sujets.


La principale différence entre ces deux instructions est que xchgl instruction n'aura aucun effet sur les drapeaux conditionnels. Certes, nous pouvons tester l'état variable de verrouillage avec Verrouillage cmpxchg instruction mais cela est encore plus complexe que verrouillage ajouter 0 $ instruction.

Autres conseils

Je cite les manuels IA32 (vol 3A, Chapitre 8.2: Commande de mémoire):

  

Dans un système mono-processeur pour les régions de mémoire définie comme cacheable écriture différée, le modèle respecte-commande de mémoire les principes suivants [..]

     
      
  • lectures ne sont pas réorganisés avec d'autres lit
  •   
  • Rédige ne sont pas réorganisés avec plus lit
  •   
  • à la mémoire Rédige ne sont pas réorganisés avec d'autres écritures, à l'exception de   
        
    • écrit exécuté avec l'instruction CLFLUSH
    •   
    • stocke en continu (écrit) exécuté avec les instructions de mouvement non-temporelles ([liste d'instructions ici])
    •   
    • les opérations de chaîne (voir la section 8.2.4.1)
    •   
  •   
  • Reads peut être réorganisés avec les anciennes écritures à différents endroits, mais pas avec les anciennes écritures au même endroit.
  •   
  • lecture ou d'écriture ne peut pas être réorganisés avec des instructions d'E / S, des instructions verrouillées ou des instructions sérialisation
  •   
  • Reads ne peut pas transmettre des instructions de LFENCE et MFENCE
  •   
  • Rédige ne peut pas transmettre des instructions de SFENCE et MFENCE
  •   

Note: « Dans un système mono-processeur » ci-dessus est légèrement trompeur. Les mêmes règles sont valables pour chaque processeur (logique) individuellement; le manuel va ensuite décrire les règles de commande supplémentaires entre plusieurs processeurs. Le seul peu à ce sujet se rapportant à la question est que

  
      
  • instructions ont Locked un ordre total.
  •   

En bref, aussi longtemps que vous écrivez à la mémoire d'écriture arrière (qui est toute la mémoire que vous verrez jamais aussi longtemps que vous n'êtes pas un programmeur de pilote ou graphiques), la plupart des instructions x86 sont presque séquentielle cohérente - la seule réordonnancement un processeur x86 peut effectuer est Réorganiser plus tard (indépendant) lit à exécuter avant les écritures. La principale chose sur les barrières d'écriture est qu'ils ont un préfixe lock (implicite ou explicite), qui interdit toute remise en ordre et assure que les opérations se voit dans le même ordre par tous les processeurs dans un système multi-processeur.

En outre, en mémoire à écriture, de lecture ne sont jamais réorganisés, il n'y a donc pas besoin de barrières de lecture. Récents processeurs x86 ont un modèle de cohérence de la mémoire plus faible pour le streaming et la mémoire stocke combinée écriture (couramment utilisée pour la mémoire graphique mappé). C'est là que les différentes instructions de fence entrent en jeu; ils ne sont pas nécessaires pour tout autre type de mémoire, mais certains pilotes dans le noyau Linux traitent la mémoire ne combinée écriture donc ils ont défini leur lecture barrière de cette façon. La liste des modèles de commande selon le type de mémoire est à la section 11.3.1 en Vol. 3A les manuels IA-32. Version courte: Write-Through, Write-Back et protégé en écriture permettent lit spéculative (suivant les règles détaillées ci-dessus), la mémoire uncachable et Strong Uncacheable a de fortes garanties de commande (pas réordonner processeur, lecture / écriture sont immédiatement exécutés, utilisés pour MMIO ) et écriture mémoire combinée a commande faible (à savoir les règles de commande détendues que les clôtures de besoin).

lock addl $0, (%esp) est un substitut à mfence, non lfence.

Le cas d'utilisation est lorsque vous devez bloquer réordonnancement StoreLoad (le seul type que solide modèle de mémoire x86 permet), mais vous n'avez pas besoin d'une opération de RMW atomique sur une variable partagée. https://preshing.com/20120515/memory-reordering-caught- in-the-act /

par exemple. en supposant que std::atomic<int> a,b alignés:

movl   $1, a             a = 1;    Atomic for aligned a
# barrier needed here
movl   b, %eax           tmp = b;  Atomic for aligned b

Vos options sont:

  • Faire un magasin séquentiel cohérence avec xchg , par exemple mov $1, %eax / xchg %eax, a de sorte que vous n'avez pas besoin d'une barrière distincte; il fait partie du magasin. Je pense que c'est l'option la plus efficace sur la plupart du matériel moderne; C ++ 11 compilateurs autres que l'utilisation de gcc xchg pour les magasins seq_cst.
  • Utilisez mfence comme une barrière. (Utilisations gcc mov + mfence pour les magasins de seq_cst).
  • Utilisation lock addl $0, (%esp) comme un obstacle. Toute instruction locked est une barrière complète. ne verrouille XCHG ont le même comportement que mfence?

    (ou à un autre endroit, mais la pile est presque toujours privé et chaud dans L1d, il est donc un candidat assez bonne. Cependant, cela peut créer une chaîne de dépendance pour quelque chose en utilisant les données au bas de la pile.)

Vous ne pouvez utiliser xchg comme une barrière en pliant dans un magasin, car il écrit inconditionnellement l'emplacement de mémoire avec une valeur qui ne dépend pas de la valeur ancienne.

Lorsque cela est possible, en utilisant xchg pour un magasin seq-cst est probablement mieux, même si elle se lit aussi de l'emplacement partagé. mfence est plus lent que prévu sur les processeurs Intel récents ( Quand dois-je utiliser _mm_sfence _mm_lfence et _mm_mfence (ma réponse et @ la réponse de BeeOnRope) pour en savoir plus sur les raisons lfence est pas utile, et quand utiliser chacune des instructions de barrière. (Ou dans le mien, le C ++ intrinsics lors de la programmation en C ++ au lieu de asm).

En aparté aux autres réponses, les devs HotSpot a constaté que lock; addl $0,0(%%esp) avec un décalage ne peut pas être zéro optimale, sur certains processeurs, il peut introduire des dépendances de données fausses ; jdk bug .

Toucher un emplacement de la pile avec un autre décalage peut améliorer les performances dans certaines circonstances.

La partie importante de lock; addl et xchgl est le préfixe lock. Il est implicite xchgl. Il n'y a vraiment pas de différence entre les deux. Je regarde comment ils se réunissent et choisir celui qui est plus courte (en octets) puisque c'est généralement plus rapide pour des opérations équivalentes sur x86 (donc des trucs comme xorl eax,eax)

La présence de SSE2 est probablement juste une approximation de la condition réelle qui est finalement une fonction de cpuid. Il se doute que SSE2 implique l'existence d'lfence et la disponibilité des SSE2 a été vérifié / mis en cache au démarrage. lfence est nécessaire quand il est disponible.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top