What is a memory model, anyway?
到底什么是内存模型?
In multiprocessor systems, processors generally have one or more layers of memory cache,
which improves performance both by speeding access to data (because the data is closer to the processor)
and reducing traffic on the shared memory bus (because many memory operations can be satisfied by local caches.)
在多处理系统上,处理器一般有一层或多层cache。它提升了访问数据的速度(由于数据离cpu更近了),
减小了共享的内存总线的拥堵(由于大多数的对内存的操做均可以在本地的cache中获得知足)
Memory caches can improve performance tremendously,
but they present a host of new challenges.
What, for example, happens when two processors examine the same memory location at the same time?
Under what conditions will they see the same value?
cache虽然极大地提升了性能,可是它带来了新的挑战。好比:当两个处理器同时检测同一个内存地址时会发生什么?在这种状况下它们会看到相同的值吗?
At the processor level,
a memory model defines necessary and sufficient conditions
for knowing that writes to memory by other processors are visible to the current processor,
and writes by the current processor are visible to other processors.
在处理器级别上,内存模型定义了知道另外一个处理器写内存,对当前处理器可见的充要条件;以及当前处理器写内存,对其余处理器可见的充要条件。
Some processors exhibit a strong memory model, where all processors see exactly the same value for any given memory location at all times.
Other processors exhibit a weaker memory model, where special instructions, called memory barriers,
are required to flush or invalidate the local processor cache in order to see writes made by other processors or make writes by this processor visible to others.
一些处理器提出了强内存模型,即全部处理器在任什么时候候,对于给定的内存地址,看到的值都是同样的。
而另外一些处理器提出了弱内存模型,即为了让其余处理器的写结果对该处理器可见,或是该处理器的写结果对其余处理器可见,须要一些特殊的指令,称做内存屏障
用来冲刷处理器的本地cache或使处理器的本地cache失效。
These memory barriers are usually performed when lock and unlock actions are taken;
they are invisible to programmers in a high level language.
这些内存屏障一般被执行在加锁或解锁的动做中,对使用高级语言的程序猿隐藏。
It can sometimes be easier to write programs for strong memory models, because of the reduced need for memory barriers.
However, even on some of the strongest memory models, memory barriers are often necessary;
quite frequently their placement is counterintuitive.
强内存模型有时会比较容易编写程序,由于减小了对内存屏障的须要。
可是就算是在某些最强的内存模型中,内存屏障经常也是须要的,它们放置的位置常常是违反直觉的。
Recent trends in processor design have encouraged weaker memory models,
because the relaxations they make for cache consistency allow for greater scalability across multiple processors and larger amounts of memory.
处理器设计的最新趋势促进了弱内存模型,由于它们放松了缓存的一致性,所以能够在多个处理器之间实现更大的可伸缩性,和使用更大容量的内存。
The issue of when a write becomes visible to another thread is compounded by the compiler's reordering of code.
For example, the compiler might decide that it is more efficient to move a write operation later in the program;
as long as this code motion does not change the program's semantics, it is free to do so.
If a compiler defers an operation, another thread will not see it until it is performed;
this mirrors the effect of caching.
编译器对代码的重排序使得写结果什么时候对另外一个线程可见的问题更加复杂。
好比,编译器可能决定将写操做移到后面会比较高效,只要代码的移动不改变的程序的语义,这样作就没什么问题。
若是编译器推迟了一个操做,在它被执行前另外一个线程就不会看到该变化,这反映出了缓存的影响。
Moreover, writes to memory can be moved earlier in a program;
in this case, other threads might see a write before it actually "occurs" in the program.
All of this flexibility is by design -- by giving the compiler, runtime, or hardware the flexibility to execute operations in the optimal order, within the bounds of the memory model, we can achieve higher performance.
另外,往内存写的操做可能被移到前面,在这种状况下,其余线程可能会看到写的结果出如今了它实际"发生"以前。
为了以最佳的顺序来执行操做,编译器、运行时或硬件会设计的比较灵活,在内存模型的边界下,咱们能够得到更高的性能。
A simple example of this can be seen in the following code:
一个简单的例子以下面代码所示:
Class Reordering {
int x = 0, y = 0;
public void writer() {
x = 1;
y = 2;
}
public void reader() {
int r1 = y;
int r2 = x;
}
}
Let's say that this code is executed in two threads concurrently, and the read of y sees the value 2. Because this write came after the write to x, the programmer might assume that the read of x must see the value 1. However, the writes may have been reordered. If this takes place, then the write to y could happen, the reads of both variables could follow, and then the write to x could take place.
The result would be that r1 has the value 2, but r2 has the value 0.
上面的代码被两个线程并发执行,读到y的值是2。由于y的值在x=1后被赋值的,所以程序猿可能会假定读x的值必然是1。
然而,写操做可能被重排序。若是发生了重排序,则y=2可能先发生,以后执行reader(),读取y和x的值,再执行x=1。
这样的结果就是r1=2,而r2=0。
The Java Memory Model describes what behaviors are legal in multithreaded code, and how threads may interact through memory.
It describes the relationship between variables in a program and the low-level details of storing and retrieving them to and from memory or registers in a real computer system.
It does this in a way that can be implemented correctly using a wide variety of hardware and a wide variety of compiler optimizations.
Java内存模型描述了多线程代码中,什么行为是合法的,以及线程如何经过内存交互。它描述了程序中变量之间的关系和对实际计算机系统的内存或寄存器存取的底层细节。
它使用各类硬件和编译器的优化,以这种方式来正确实现上述说明。
Java includes several language constructs, including volatile, final, and synchronized,
which are intended to help the programmer describe a program's concurrency requirements to the compiler.
The Java Memory Model defines the behavior of volatile and synchronized, and, more importantly, ensures that a correctly synchronized Java program runs correctly on all processor architectures.
Java包括几个语言结构,volatile、final和synchronized,这些结构倾向于帮助程序猿对编译器描述程序的并发需求。
更重要的是,Java内存模型定义了volatile和synchronized的行为,确保正确同步的Java程序在全部处理器体系结构上都能正确运行。
Do other languages, like C++, have a memory model?
其余语言,如C++,有内存模型吗?
Most other programming languages,
such as C and C++, were not designed with direct support for multithreading.
The protections that these languages offer against the kinds of reorderings that take place in compilers and architectures are heavily dependent on the guarantees provided by the threading libraries used (such as pthreads), the compiler used, and the platform on which the code is run.
大多数其余编程语言,如C/C++,没有设计直接支持多线程。这些语言提供针对编译器和体系结构发生重排序的保护措施严重依赖所使用的多线程库(好比pthreads)、编译器、和代码运行的平台的保证。
[注] C++11也提出了内存模型。