2017-07-17 2 views
1

J'ecrivais ce poste dans le cadre de Deep understanding of volatile in JavaAnalyse de la production x86 générée par JIT dans le contexte de volatilité

public class Main { 
    private int x; 
    private volatile int g; 


    public void actor1(){ 
     x = 1; 
     g = 1; 
    } 


    public void actor2(){ 
     put_on_screen_without_sync(g); 
     put_on_screen_without_sync(x); 
    } 
} 

Maintenant, j'analyse ce JIT généré pour pièce au-dessus du code. De notre discussion dans mon post précédent, nous savons que la production 1, 0 est impossible parce que:


écrire à v volatile provoque que chaque action a précède v fait que a sera visible (sera rincée à la mémoire) avant v sera Être visible.


.................(I removed not important body of method)..... 

    0x00007f42307d9d5e: c7460c01000000  (1) mov  dword ptr [rsi+0ch],1h 
               ;*putfield x 
               ; - package.Main::[email protected] (line 14) 

    0x00007f42307d9d65: bf01000000   (2) mov  edi,1h 
    0x00007f42307d9d6a: 897e10    (3) mov  dword ptr [rsi+10h],edi 
    0x00007f42307d9d6d: f083042400   (4) lock add dword ptr [rsp],0h 
               ;*putfield g 
               ; - package.Main::[email protected] (line 15) 

    0x00007f42307d9d72: 4883c430   add  rsp,30h 
    0x00007f42307d9d76: 5d     pop  rbp 
    0x00007f42307d9d77: 850583535116  test  dword ptr [7f4246cef100h],eax 
               ; {poll_return} 
    0x00007f42307d9d7d: c3     ret 

Est-ce que je comprends bien que cela fonctionne parce que x86 ne peut pas faire StoreStore réordonnancement? Si cela pouvait nécessiter une barrière de mémoire supplémentaire, oui?


ÉDITÉE APRÈS EXCELLENT @ réponse Eugene:

int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

Ici, je vois ce que vous dire- il est clair: every action below (after) lecture volatile (int tmp = i) ne sera pas réorganisés.

// [StoreLoad] -- this one 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

Ici, vous mettre une barrière plus. Il nous assure qu'aucune action ne sera réorganisée avec int tmp = i. Mais, pourquoi est-ce important? Pourquoi j'ai des doutes? De ce que je sais volatile load garanties:

Chaque action après charge volatile ne sera pas réorganisé avant que la charge volatile est visible.

Je vois que vous écrivez:

Il doit y avoir une cohérence séquentielle

Mais, je ne vois pas pourquoi est nécessaire cohérence séquentielle.

+0

Qu'est-ce que 'a'? Quel 'v'? Voulez-vous dire «x» et «g»? – Andreas

+0

Maintenant, 'a' est une action au-dessus de' v'- par exemple c'est une action: 'x = 1'. 'v' est un magasin:' g = 1' – Gilgamesz

+0

Le JMM n'était pas fait pour x86 ou toute autre architecture spécifique et ne raisonne pas en termes de charge ou de magasin. Il est de la responsabilité d'une JVM de mettre en œuvre le JMM avec les instructions disponibles sur chaque architecture. – assylias

Répondre

3

Un couple de choses, d'abord will be flushed to memory - c'est assez erroné. C'est presque jamais une couleur de la mémoire principale - il draine habituellement StoreBuffer à L1 et c'est au protocole de cohérence de cache de synchroniser les données entre toutes les caches, mais s'il vous est plus facile de comprendre ce concept en ces termes, c'est bien - Sachez que c'est légèrement différent et plus rapide.

C'est une bonne question de savoir pourquoi le [StoreLoad] est là en effet, peut-être que cela éclaircira un peu les choses. volatile est en effet tout au sujet des clôtures.Voici un exemple de ce qui se passerait:

int tmp = i; // volatile load 
    // [LoadStore] 
    // [LoadLoad] 

que ce qui se passe pour un volatile load et au-dessous est ce qui se passe pour une mémoire volatile:

// [StoreStore] 
// [LoadStore] 
i = tmp; // volatile store 

Mais qui est pas, il y a plus. Il doit y avoir un sequential consistency, c'est pourquoi une mise en œuvre saine garantit que volatile eux-mêmes ne sont pas réorganisées, donc deux autres clôtures sont insérés:

// [StoreLoad] -- this one 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

Et encore ici:

// [StoreStore] 
// [StoreLoad] 
i = tmp; // volatile store 
// [StoreLoad] -- and this one 

Maintenant, il s'avère que sur x86 3 barrages de mémoire sur 4 sont libres - puisqu'il s'agit d'un strong memory model. Le seul qui doit être implémenté est StoreLoad.

Habituellement un mfence est une bonne option pour StoreLoad sur x86, mais la même chose est garanti par lock add qui est la raison pour laquelle vous voyez là. fondamentalement que est la barrière StoreLoad. Et oui - vous avez raison dans votre dernière phrase, pour un modèle de mémoire plus faible - la barrière StoreStore serait nécessaire. Sur une note, c'est ce qui est utilisé lorsque vous publiez en toute sécurité une référence via final champs à l'intérieur d'un constructeur. À la sortie du constructeur, deux clôtures sont insérées: LoadStore et StoreStore.

EDIT

Supposons que vous ayez ce cas:

[StoreStore] 
[LoadStore] 
int x = i; // volatile store 

int j = 3; // plain actions 

j = x; // volatile load 
[LoadLoad] 
[LoadStore] 

Fondamentalement, il n'y a aucune barrière qui empêcherait le volatile store d'être commandés avec le volatile load (ie: la charge volatile serait effectué d'abord) et cela causerait évidemment des problèmes; consistance séquentielle étant ainsi violée.

Vous êtes en quelque sorte manquant le point ici btw (si je ne me trompe pas) via Every action after volatile load won't be reordered before volatile load is visible. Le ré-ordonnancement n'est pas possible avec le volatile lui-même - d'autres opérations peuvent être réapprovisionnées. Permettez-moi de vous donner un exemple:

int y = 0; 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

int x = 3; // plain load 
y = 4; // plain store 

Les deux dernières opérations x = 3 et y = 4 sont absolument libres d'être commandés, ils ne peuvent pas flotter au-dessus du volatile, mais ils peuvent être commandés par eux-mêmes . L'exemple ci-dessus serait parfaitement légal:

int y = 0; 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

// see how they have been inverted here... 
y = 4; // plain store 
int x = 3; // plain load 
+0

merci pour votre réponse impressionnante. J'ai édité mon post et demander un détail. – Gilgamesz

+0

@Gilgamesz édité – Eugene

+0

merci beaucoup! :) – Gilgamesz