大免责声明
一些我在这里写的事情实际上测试 - 就像重新排序,冲洗等;他们中的一些人花了很多时间阅读,我希望我说得对。
一切被重新排序,而不是重新排序的策略,让你的程序运行它在数年前下降了道路。只要输出不改变,操作就会按照他们的要求重新排序。
例如:
static int sum(int x, int y){
x = x + 1;
y = y + 1;
return x + y;
}
你真的不关心为了在实现这些工作,只要结果是正确的,你做了什么?
没有内存屏障(通常称为StoreLoad|StoreStore|LoadStore|LoadLoad
),任何操作都可能发生变化。为了保证有些操作不需要move beyond a fence
,有cpu fences
执行。 Java有几种生成方法 - volatile
,synchroniztion
,Unsafe/VarHandle
(可能有其他的,我不知道)。
基本上当你写一个volatile例如,出现这种情况:
volatile x...
[StoreStore] - inserted by the compiler
[LoadStore]
x = 1; // volatile store
[StoreLoad]
...
[StoreLoad]
int t = x; // volatile load
[LoadLoad]
[LoadStore]
让我们这个例子的一个子集:
这意味着任何Store
或的Load
变量不能用x = 1
重新排序。同样的原则适用于其他障碍。
马丁汤姆森说,那是什么on x86
4分之3的屏障是免费的,已被发出的只有一个:StoreLoad
。它们是免费的,因为x86具有强大的内存模型,这意味着其他操作默认情况下不会重新排序。在其他的CPU上,其中一些操作也相当便宜(如果我在ARM
上有错误lwsync
- 轻量级同步;名称应该是自解释的)。
此外,CPU和缓存之间还有一点缓冲区 - 称为Store Buffer
。当您将某些内容写入变量时,它不会直接进入缓存(s)。它进入该缓冲区。当它已满(或被强制通过StoreLoad
排空)时,它会将写入写入缓存 - 并且最多可以使用cache coherency protocol
来同步所有缓存中的数据。
马丁说,如果你有多个作家,你必须多次发出StoreLoad
- 因此它是昂贵的。如果你有一个作家,你不需要。缓冲区满时会排空。什么时候发生?那么有时,理论上可能永远不会,实际上相当快。
一些很棒的资源(这些资源有时让我整夜都没有睡觉,所以小心!):
这些一StoreStore
顺便说一句你写的每一个最终的变量在构造函数中时间:
private final int i ;
public MyObj(int i){
this.i = i;
// StoreStore here
}
LazySet
Shipilev Volatile
And my all time favorite!
您使用的是Java。您应该尊重Java内存模型。不是x86/64内存模型。 –