7

众所周知,Java 语言允许编译器重新排列已编译代码的行,只要重新排序对代码语义没有影响。然而,编译器只需要关心从当前线程看到的语义。如果这种重新排序影响多线程情况下的语义,通常会导致并发问题(内存可见性)

我的问题:

  1. 允许编译器释放这个 freedm 可以实现什么?编译器真的有可能通过重新排列代码来生成更高效的代码吗?我还没有看到一个实际的案例。我有时觉得,如果有的话,它可能带来的并发风险远远超过了好处。

  2. 程序员有什么办法可以告诉编译器不要像这样重新排列行?我知道使用同步原语有效地处理重新排列的副作用,但我问是否有任何直接的方法(编译器选项)来关闭它?

4

3 回答 3

8

编译器javac几乎没有执行任何优化。

JIT 本机编译器可以在存在内存排序问题的情况下重新排序指令。但是,CPU 也可以重新排序指令和内存更新,它们具有相同的效果。

允许编译器释放这个 freedm 可以实现什么?

主要好处是代码可移植性。你提供的保证越多,就越难确保每个平台都真正做到这一点。

通过允许 CPU 尽可能而不是按严格的顺序执行指令,还可以显着提高性能。

编译器真的有可能通过重新排列代码来生成更高效的代码吗?

是的。但是 CPU 进行的重新排序更为重要。

我还没有看到一个实际的案例。我有时觉得,如果有的话,它可能带来的并发风险远远超过了好处。

程序员有什么办法可以告诉编译器不要像这样重新排列行?

这就是为什么你使用内存屏障,例如volatilesynchronized块和Lock. 当您使用这些时,您将获得线程安全保证。

我知道使用同步原语有效地处理重新排列的副作用,但我问是否有任何直接的方法(编译器选项)来关闭它?

您可以关闭 JIT,但大多数重新排序是由 CPU 完成的,因此不会有太大的作用。

避免重新排序更新只是线程安全问题的一小部分(它最大的问题是晦涩难懂且很少发生,这使得测试变得困难)一旦编写线程安全代码,这种情况就会得到缓解。

于 2012-01-05T12:22:20.197 回答
2

编译器真的有可能通过重新排列代码来生成更高效的代码吗?

哦,是的!

现代计算机体系结构中的生活事实之一是内存是主要的性能瓶颈。CPU 执行寄存器到寄存器指令的速度比它读取和写入主存储器的速度快很多倍。这就是为什么高性能芯片具有 2 或 3 级内存缓存的原因。此外,典型的 CPU 使用流水线允许多条指令同时处于执行过程中。

为了从具有这些属性的 CPU 中获得最大性能,编译器(以及 CPU 本身)需要能够重新排序指令以充分利用内存带宽,并保持流水线满载。本机代码还需要能够使用保存在寄存器中的值(变量),并尽量减少等待内存写入传播到主内存的内存屏障指令的使用。仅允许(在 Java 中)这些是因为 Java 内存模型允许重新排序。

注意:我们在这里谈论的是显着的加速:可能是 3 到 5 倍的加速。

如果您想了解多少,请使用一个实例变量密集型应用程序并重写它,以便所有实例变量都是volatile. (这将减少重新排序的范围,并导致所有读取和写入都进入内存。)然后将原始应用程序的性能与修改后的版本进行基准测试。

于 2012-01-05T12:56:11.470 回答
2

重新排列字节码的过程由 JIT(即时编译器)管理。您可以使用以下选项阻止它运行:

-Djava.compiler=NONE

在此处查看更多信息:http ://www.cs.swarthmore.edu/~newhall/unixhelp/debuggingtips_Java.html

于 2012-01-05T12:21:24.887 回答