Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java, volatile and memory barriers on x86 architecture

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?

like image 209
kRs Avatar asked Apr 23 '16 13:04

kRs


Video Answer


1 Answers

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 StoreLoads after each one of them there's a StoreStore (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.

like image 87
Dimitar Dimitrov Avatar answered Oct 23 '22 04:10

Dimitar Dimitrov