我研究了Java内存模型并看到了重新排序问题.一个简单的例子:
boolean first = false;
boolean second = false;
void setValues() {
first = true;
second = true;
}
void checkValues() {
while(!second);
assert first;
}
Run Code Online (Sandbox Code Playgroud)
重新排序是非常不可预测和奇怪的.此外,它破坏了抽象.我认为处理器架构必须有充分的理由去做一些对程序员来说太不方便的事情. 这些原因是什么?
有很多关于如何处理重新排序的信息,但我找不到任何关于它为什么需要的信息.在任何地方,人们只会说"这是因为一些性能优势".例如,second之前存储的性能优势是first什么?
您能推荐一些关于此的文章,论文或书籍,或者自己解释一下吗?
java optimization multithreading cpu-architecture compiler-optimization
据我了解,当 CPU 推测性地执行一段代码时,它会在切换到推测性分支之前“备份”寄存器状态,以便如果预测结果错误(使分支无用)——寄存器状态将是安全恢复,而不会破坏“状态”。
所以,我的问题是:推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?
我的意思是,访问 RAM 不是“原子”操作——如果数据当前不在 CPU 缓存中,那么从内存中读取一个简单的操作码可能会导致实际的 RAM 访问,这可能会变成一个非常耗时的操作,从 CPU 的角度来看。
如果在推测分支中确实允许这种访问,它是否仅用于读取操作?因为,我只能假设,如果一个分支被丢弃并执行“回滚”,根据它的大小恢复写操作可能会变得非常缓慢和棘手。而且,可以肯定的是,至少在某种程度上支持读/写操作,因为寄存器本身,在某些 CPU 上,据我所知,物理上位于 CPU 缓存上。
所以,也许更精确的表述是:推测执行的一段代码有什么限制?
在我的理解中,CPU为了优化而改变了写在机器代码上的操作顺序,这被称为乱序执行。
在术语“内存顺序”中,它定义了访问内存的顺序。例如,在宽松的顺序中,它定义了非常弱的排序规则,并且很容易发生执行重排序。
x86 中有一些内存排序模型,例如 TSO。在这样的存储器排序模型中,定义了处理器的存储器访问顺序的语义。
我不明白的是他们之间的关系。内存顺序是一种乱序执行吗?OoOe还有其他方法吗?
或者说,内存顺序是乱序执行的实现,处理器的所有重新排序都是基于语义的吗?
我写了一个多线程程序来演示英特尔处理器的乱序效果.该计划附在本文末尾.预期的结果应该是当handler1将x打印为42或0时.但是,实际结果总是为42,这意味着不会发生乱序效应.
我使用命令"gcc -pthread -O0 out-of-order-test.c"编译了程序.我在Intel IvyBridge处理器Intel(R)上运行Ubuntu 12.04 LTS(Linux内核3.8.0-29-通用)上的编译程序)Xeon(R)CPU E5-1650 v2.
有谁知道我应该怎么做才能看到乱序效果?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int f = 0, x = 0;
void* handler1(void *data)
{
while (f == 0);
// Memory fence required here
printf("%d\n", x);
}
void* handler2(void *data)
{
x = 42;
// Memory fence required here
f = 1;
}
int main(int argc, char argv[])
{
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, handler1, NULL);
pthread_create(&tid2, NULL, handler2, NULL);
sleep(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)