Short answer:
Yes, this optimization is allowed. Coagulation of two consecutive read operations leads to the observed behavior of the sequence, which is atomic, but does not look like a reordering of operations. Any sequence of actions performed in a single thread of execution can be performed as an elementary element. In general, it is difficult to provide an atomic sequence of operations, and this rarely leads to an increase in productivity, since in most runtime environments the overhead of atomic execution is imposed.
In the example given in the original question, the sequence of operations considered is as follows:
read(a) read(a)
Performing these operations atomically ensures that the value read in the first line is equal to the value read in the second line. In addition, this means that the value read in the second line is the value contained in a during the first read (and vice versa, since both atomic read operations occurred simultaneously in accordance with the observed program execution state), Considered optimization, which reuses the value of the first read for the second read is equivalent to the compiler and / or JIT executing the sequence atomically, and thus is valid.
Original longer answer:
The Java memory model describes operations using the "occurs before" partial ordering. To express the limitation that the first read of r1 and the second read of r2 a cannot be collapsed, you need to show that some operation is semantically required between them.
The operations on the stream with r1 and r2 as follows:
To express the requirement that something (say y ) lie between r1 and r2 , you need to require that r1 happen before y and y happen before r2 . As it happens, there is no rule where a read operation appears on the left side of the "occurs before" relationship. The closest thing you could get is to say that y happens before r2 , but a partial order would let y happen before r1 , thereby folding read operations.
If there is no scenario that requires the operation to be between r1 and r2 , then you can declare that an operation never appears between r1 and r2 , and not violate the required semantics of the language. Using a single read operation will be equivalent to this statement.
Edit My answer is rejected, so I'm going to go into further details.
Here are some related questions:
Is a Java or JVM compiler required to collapse these read operations?
No. The expressions a and a used in the add expression are not constant expressions, so they are not required to be collapsed.
Does the JVM collapse these read operations?
To this I am not sure of the answer. javap -c program and using javap -c , it is easy to see that the Java compiler does not collapse these read operations. Unfortunately, proving that the JVM does not disrupt the operation (or even the processor itself) is not so simple.
Should the JVM collapse these read operations?
Probably no. Each optimization takes time to complete, so there is a balance between the time taken to analyze the code and the expected benefit. Some optimizations, such as eliminating array bounds checking or checking for null references, have proven to have extensive advantages for real-world applications. The only case where this particular optimization has the ability to improve performance is when two identical read operations appear sequentially.
In addition, as shown in the answer to this answer, along with other answers, this particular change may lead to an unexpected change in behavior for some applications that users may not want.
Edit 2: Regarding Raphael, the description of the claim is that there are two read operations that cannot be reordered. This expression is intended to emphasize the fact that caching a read operation in the following sequence may produce an incorrect result: a
a1 = read(a) b1 = read(b) a2 = read(a) result = op(a1, b1, a2)
Suppose initially a and b have a default value of 0. Then you only execute the first read(a) .
Now suppose another thread executes the following sequence:
a = 1 b = 1
Finally, suppose the first thread executes the read(b) . If you were to cache the initially read value of a , you would receive the following call:
op(0, 1, 0)
It is not right. Since the updated value of a was saved before writing to b , it is impossible to read the value of b1 = 1 and then read the value of a2 = 0 . Without caching, the correct sequence of events leads to the next call.
op(0, 1, 1)
However, if you ask the question “Is there a way to enable read caching of a ?”, The answer is yes. If you can perform all three read operations in the first sequence of threads as an elementary element, then value caching is allowed. Although synchronization between several variables is difficult and rarely gives an advantage in opportunistic optimization, it is possible that an exception will arise. For example, suppose and a b are every 4 bytes, and they appear in memory sequentially with aligned 8 bytes. a 64-bit process can implement the read(a) read(b) sequence as an atomic 64-bit load operation that will cache the value of a (effectively treating all three read operations as an atomic operation, and not just the first two).