gpt4 book ai didi

java - 分析 volatile 上下文中 JIT 生成的 x86 输出

转载 作者:行者123 更新时间:2023-11-29 08:34:36 25 4
gpt4 key购买 nike

我写这篇文章与 Deep understanding of volatile in Java 有关

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);
}
}

现在,我正在分析 JIT 为上述代码生成的内容。根据我们在上一篇文章中的讨论,我们知道输出 1, 0是不可能的,因为:

写入 volatile v导致每一个 Action a前文 v导致 av 之前可见(将被刷新到内存中)将可见。
   .................(I removed not important body of method).....

0x00007f42307d9d5e: c7460c01000000 (1) mov dword ptr [rsi+0ch],1h
;*putfield x
; - package.Main::actor1@2 (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::actor1@7 (line 15)

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

我是否正确理解它可以工作,因为 x86 无法生成 StoreStore重新排序?如果可以的话,它需要额外的内存屏障,是吗?

在优秀@Eugene 的回答之后编辑:

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


在这里,我明白你的意思了——很清楚: every action below (after) volatile 读取 ( int tmp = i ) 不会重新排序。

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


在这里,你又设置了一个障碍。它确保我们不会使用 int tmp = i 重新排序任何操作.但是,为什么它很重要?为什么我有疑问?据我所知 volatile load保证:

每一个 Action 之后 在 volatile load 可见之前,不会对 volatile load 重新排序。

我看到你写:

There needs to be a sequential consistency



但是,我不明白为什么需要顺序一致性。

最佳答案

有几件事,首先will be flushed to memory - 这是非常错误的。它几乎从不刷新到主内存 - 它通常将 StoreBuffer 耗尽到 L1并且由缓存一致性协议(protocol)在所有缓存之间同步数据,但是如果您更容易从这些术语中理解这个概念,那很好 - 只要知道它略有不同并且更快。

这是一个很好的问题,为什么 [StoreLoad]确实存在,也许这会澄清一些事情。 volatile确实都是关于栅栏的,这里是一个例子,说明在一些不稳定的操作的情况下会插入什么障碍。例如,我们有一个 volatile load :

  // i is some shared volatile field
int tmp = i; // volatile load of "i"
// [LoadLoad|LoadStore]

注意这里的两个障碍 LoadStoreLoadLoad ;用简单的英语表示任何 LoadStorevolatile load/read 之后不能“向上移动”的障碍,就不能重新排序“高于”那个不稳定的负载。

这是 volatile store 的示例.
 // "i" is a shared volatile variable
// [StoreStore|LoadStore]
i = tmp; // volatile store

这意味着任何 LoadStore不能“低于”负载存储本身。

这基本上建立了发生之前的关系, volatile load是获取负载和 volatile store作为发布存储(这也与 StoreLoad cpu 缓冲区的实现方式有关,但这几乎超出了问题的范围)。

如果你想一想,这对我们所知道的 volatile 的事情非常有意义。一般来说;它表示一旦 volatile 负载观察到 volatile 存储, volatile store 之前的所有内容也将被观察到,这与内存屏障相当。现在有道理的是,当发生 volatile 存储时,它上面的所有内容都不能超过它,而一旦发生 volatile 加载,它下面的所有内容都不能超过它,否则 this happens-before 将被破坏。

但不仅如此,还有更多。需要有顺序一致性,这就是为什么任何理智的实现都会保证 volatile 本身不会重新排序,因此又插入了两个栅栏:
 // any store of some other volatile
// can not be reordered with this volatile load
// [StoreLoad] -- this one
int tmp = i; // volatile load of a shared variable "i"
// [LoadStore|LoadLoad]

这里还有一个:
// [StoreStore|LoadStore]
i = tmp; // volatile store
// [StoreLoad] -- and this one

现在,事实证明在 x86 4 个内存屏障中有 3 个是免费的 - 因为它是 strong memory model .唯一需要实现的是 StoreLoad .在其他 CPU 上,例如 ARM例如, lwsycn是使用的一条指令-但我对它们知之甚少。

通常是 mfenceStoreLoad 的不错选择在 x86 , 但同样的事情通过 lock add 得到保证(AFAIK 以更便宜的方式),这就是你在那里看到它的原因。基本上就是 StoreLoad屏障。是的 - 你的最后一句话是对的,对于一个较弱的内存模型 - StoreStore需要屏障。附带说明一下,当您通过 final 安全地发布引用时使用的内容构造函数中的字段。退出构造函数后,插入了两个栅栏: LoadStoreStoreStore .

对这一切持保留态度——只要不违反任何规则,JVM 可以自由地忽略这些:Aleksey Shipilev 对此进行了精彩的讨论。

编辑

假设您有这种情况:
[StoreStore|LoadStore]
int x = 4; // volatile store of a shared "x" variable

int y = 3; // non-volatile store of shared variable "y"

int z = x; // volatile load
[LoadLoad|LoadStore]

基本上没有任何障碍可以阻止 volatile store使用 volatile load 重新订购(即:将首先执行 volatile 负载),这显然会导致问题;因此违反了顺序一致性。

通过 Every action after volatile load won't be reordered before volatile load is visible,顺便说一句(如果我没记错的话),你有点错过了这一点。 . volatile 本身无法重新排序 - 其他操作可以自由重新排序。让我给你举个例子:
 int tmp = i; // volatile load of a shared variable "i"
// [LoadStore|LoadLoad]

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

最后两个操作 x = 3y = 4完全可以重新排序,它们不能 float 在 volatile 之上,但可以通过它们自己重新排序。上面的例子是完全合法的:
 int tmp = i; // volatile load
// [LoadStore|LoadLoad]

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

关于java - 分析 volatile 上下文中 JIT 生成的 x86 输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45151763/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com