This is more of a theoretical question. I'm not sure if all concepts, compiler behaviors, etc. are uptodate and still in use, but I'd like to have confirmation if I'm correctly understanding some concepts I'm trying to learn.
Language is Java.
From what I've understood so far, on X86 architecture, StoreLoad barriers (despite the exact CPU instructions used to implement them) are put after Volatile writes, to make them visible to subsequent Volatile Reads in other threads (since x86 doesn't guarantee that newer reads always see older writes) (reference http://shipilev.net/blog/2014/on-the-fence-with-dependencies/)
Now from here (http://jpbempel.blogspot.it/2013/05/volatile-and-memory-barriers.html) I see that:
public class TestJIT
{
private volatile static int field1;
private static int field2;
private static int field3;
private static int field4;
private static int field5;
private volatile static int field6;
private static void assign(int i)
{
field1 = i << 1; // volatile
field2 = i << 2;
field3 = i << 3;
field4 = i << 4;
field5 = i << 5;
field6 = i << 6; // volatile.
}
public static void main(String[] args) throws Exception
{
for (int i = 0; i < 10000; i++)
{
assign(i);
}
Thread.sleep(1000);
}
}
the resulting assembly has the StoreLoad only after field6 assignment, and not after field1 assignment which however is volatile as well.
My questions:
1) Does what I have written so far make sense? Or am I totally misinterpreting something?
2) Why is the compiler omitting a StoreLoad after field1 volatile assignment? Is this an optimization? But has it some drawbacks? For example, another thread kicking in after field1 assignment, might still read an old value for field1 even if it has been actually changed?
1) Does what I have written so far make sense? Or am I totally misinterpreting something?
I think you got everything correct.
2) Why is the compiler omitting a StoreLoad after field1 volatile assignment? Is this an optimization? But has it some drawbacks?
Yes, it's an optimization, but it's a pretty tricky one to get right.
Doug Lea's JMM Cookbook actually shows an example of the recommended barriers in the case of two consecutive volatile
stores, and there are there's a StoreLoad
s after each one of themStoreStore
(x86 no-op) between the two stores and a StoreLoad
only after the second one. The Cookbook however notes that the related analysis can be fairly involved.
The compiler should be able to prove that a volatile
read cannot occur in the synchronization order between the write to field1
and the write to field6
. I'm not sure if that's doable (by the current HotSpot JIT) if TestJIT
was changed slightly so that a comparable amount of volatile
loads is executed in another thread at the same time.
For example, another thread kicking in after field1 assignment, might still read an old value for field1 even if it has been actually changed?
That should not be allowed to happen, if that volatile
load follows the volatile
store in the synchronization order. So as mentioned above, I think that the JIT gets away with it, because it doesn't see any volatile
loads being done.
Update
Changed the details around the JMM Cookbook example, as kRs pointed out that I've mistook a StoreStore
for a StoreLoad
. The essence of the answer was not changed at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With