Java memory model : compiler rearranging code line

2019-03-20 00:42发布

问题:

It is well known that Java Language allows compliers to re-arrange lines of compiled code as long as the re-order makes no difference to the code semantics. However , the compiler is required to only bother about sematics as seen from the current thread. If this re-order affects semantics in a multithreaded situation , it usually causes concurrency issues ( memory visibility )

My question(s) :

  1. What is achieved by allowing this freedm to the compiler ? Is it really possible for the compiler to produce code which is more efficient by rearranging the code ? I am yet to see a practical case for this. I feel sometimes that the benefits if any are far outweighed by the concurrency risks this can introduce.

  2. Is there any way that programmer can tell compiler not to rearrange lines like this ? I know that using synchronization primitives effectively handles the side-effects of rearranging , but I am asking if there is any direct way ( compiler option ) to turn this off ?

回答1:

The javac compiler has performs next to no optimisations.

The JIT native compiler could re-order instructions where there is a memory ordering problem. However, the CPU also can re-order instructions and memory updates which have the same effect.

What is achieved by allowing this freedm to the compiler ?

The main benefit is code portability. The more guarantees you provide, the more difficult it is to ensure every platform actually does this.

There is also a significant performance improvement by allowing the CPU to execute instructions as and when it can rather than in a strict order.

Is it really possible for the compiler to produce code which is more efficient by rearranging the code ?

Yes. but the re-ordering done by the CPU is more significant.

I am yet to see a practical case for this. I feel sometimes that the benefits if any are far outweighed by the concurrency risks this can introduce.

Is there any way that programmer can tell compiler not to rearrange lines like this ?

This is why you use memory barriers like volatile, synchronized blocks and Lock. When you use these you get thread safety guarantees.

I know that using synchronization primitives effectively handles the side-effects of rearranging , but I am asking if there is any direct way ( compiler option ) to turn this off ?

You can turn off the JIT, but most re-ordering is done by the CPU so it wouldn't achieve much.

Avoiding re-ordering of updates is such a small part of the thread safety problem (its biggest issue is that is obscure and rarely occurs which makes testing it hard) And once you write thread safe code, this is alleviated.



回答2:

The process of re-arranging the bytecode is managed by the JIT (Just in Time compiler). You can stop it from running with the following option:

-Djava.compiler=NONE

See more here: http://www.cs.swarthmore.edu/~newhall/unixhelp/debuggingtips_Java.html



回答3:

Is it really possible for the compiler to produce code which is more efficient by rearranging the code ?

Oh yes it is!

One of the facts of life in modern computer architecture is that memory is the main performance bottleneck. The CPU can perform register to register instructions many times faster than it can read from and write to main memory. That's why high performance chips have 2 or 3 levels of memory cache. In addition, typical CPUs use pipelining allow multiple instructions to be in the process of being executed at the same time.

To get the most performance out of CPUs with these properties, the compiler (and the CPU itself) need to be able to reorder instructions make best use of the memory bandwidth, and keep the pipeline full. The native code also needs to be able to use of values (of variables) saved in registers, and minimize the use of memory barrier instructions that wait for memory writes to propagate to main memory. These are only allowed (in Java) because the Java memory model permits reordering.

Note: we are talking about a significant speedup here: maybe a 3 to 5-fold speedup.

If you want to get a feel for how much, take an instance variable intensive application and rewrite it so that all instance variables are volatile. (This will reduce the scope for reordering, and cause all reads and writes to go to memory.) Then benchmark the performance of the original application against the modified version.