none
questions for VolatileWrite and VolatileRead RRS feed

  • Question

  • 1, For the VolatileWrite , why it call the Thread.MemoryBarrier(); before the assignment "address = value;"? it's ok for calling the Thread.MemoryBarrier(); after the assignment "address = value;"?

    The same question for VolatileRead, it's ok for calling the Thread.MemoryBarrier(); before the assignment "object arg_07_0 = address;"?

    [CLSCompliant(false)]
    [MethodImpl(MethodImplOptions.NoInlining)]
    publicstaticvoidVolatileWrite(refuint address, uintvalue)
    {
        Thread.MemoryBarrier();
        address = value;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static object VolatileRead(ref object address)
    {
        object arg_07_0 = address;
        Thread.MemoryBarrier();
        return arg_07_0;
    }

    2,From the URL below it says the Thread.MemoryBarrier Refreshes thread cache before the  operation and Flushes thread cache after operation ? what's the meaning? It means both the   _answer1 and   _answer2 would flush the thread cache to main memory After running the A() below?

      void A()
      {
        _answer1 = 1; 
        Thread.MemoryBarrier();      
         _answer2= 1; 
      }

    Construct Refreshes thread cache before? Flushes thread cache after? Notes
    Thread.MemoryBarrier Yes Yes Special memory barrier method

    http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/


    • Edited by Jacky_shen Thursday, May 5, 2016 3:37 PM
    Thursday, May 5, 2016 3:16 PM

All replies

  • I'll start by saying I'm not an expert on this 'deep-level' kind of stuff!

    But VolatileRead and VolatileWrite are used to read a field knowing that it is definitely the latest value written there by any other processor or write to a field such that the value becomes immediately accessible by any other processor.

    This becomes important on a multi-processor system as individual processors may very well cache values (even a single processor system may cache values in an internal register).

    The reason for the MemoryBarrier is because processors may very well optimize code by re-ordering instructions (to minimize the amount of times it has to swap memory in and out of registers for example). But for VolatileRead/VolatileWrite to work, the system needs to ensure that this kind of re-ordering won't occur around these calls and break this functionality.

    It forces certain things to happen in a known order in other words, even in a parallel multi-processor environment.

    At least that is my limited understanding :)

    Thursday, May 5, 2016 3:30 PM
  • Hi RJP1973

    Thank you for your reply, unfortunately you didn't answer my questions.

    Thursday, May 5, 2016 3:55 PM
  • Hi RJP1973

    Thank you for your reply, unfortunately you didn't answer my questions.

    Well, to be fair, that may partly be because you edited your question while I was in the process of responding to the original post. Maybe I jumped in a bit too quick!

    Edit: Although after re-reading I'm not quite sure I understand else you are asking.

    • Edited by RJP1973 Thursday, May 5, 2016 4:29 PM
    Thursday, May 5, 2016 4:26 PM
  • Hi RJP1973

    Thank you for your reply, unfortunately you didn't answer my questions.

    Well, to be fair, that may partly be because you edited your question while I was in the process of responding to the original post. Maybe I jumped in a bit too quick!

    Edit: Although after re-reading I'm not quite sure I understand else you are asking.

    First question is why calling Thread.MemoryBarrier(); before the assignment "address = value;"  for VolatileWrite ?

    publicstaticvoid VolatileWrite(refuint address, uintvalue)
    {
        Thread.MemoryBarrier();
        address = value;
    }

    The above code got from the ILSPY for the method VolatileWrite.


    Why VolatileWrite not implemented like below , that is to say what's the issue if  call Thread.MemoryBarrier(); after assignment "  address = value;"?

    publicstaticvoid VolatileWrite(refuint address, uintvalue)
    {

      address = value;

    Thread.MemoryBarrier();
     }

    The above code modified by me by switching the sequence for the two lines in method VolatileWrite.





    • Edited by Jacky_shen Thursday, May 5, 2016 5:02 PM
    Thursday, May 5, 2016 4:53 PM
  • "Why VolatileWrite not implemented like below , that is to say what's the issue if  call Thread.MemoryBarrier(); after assignment "  address = value;"?"

    Because such an implementation would be incorrect. A volatile store is supposed to have release semantics, that means that no loads/stores that happen before the volatile store can move after it. By placing the barrier after the volatile store you're allowing loads/stores to move between the volatile store and the barrier.

    Thursday, May 5, 2016 5:45 PM
    Moderator
  • "Why VolatileWrite not implemented like below , that is to say what's the issue if  call Thread.MemoryBarrier(); after assignment "  address = value;"?"

    Because such an implementation would be incorrect. A volatile store is supposed to have release semantics, that means that no loads/stores that happen before the volatile store can move after it. By placing the barrier after the volatile store you're allowing loads/stores to move between the volatile store and the barrier.


    Hi Mike,

    Thank you so much for your answer.

    1,

    Your answer make sense for volatile store  from the release semantics perspective , however from the articles below, we know volatile store caused the thread cache flushed to main memory after the operation, so if placing Thread.MemoryBarrier() before assignment "address = value;" , who would help to flush thread cache  to main memory ?

    As after the assignment "address = value;" , there is not statements following it to help to flush thread cache  to main memory , so how to make sure volatile store would flush thread cache  to main memory ?

    If placing Thread.MemoryBarrier() after assignment "address = value;" (although it obey the release semantics), I think Thread.MemoryBarrier() maybe help to flush the value from the thread cache to main memory , please correct me If I am wrong.

    http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/

    2,

    The same question to volatile load, if Thread.MemoryBarrier();locating after the volatile load, ,how to make sure Refreshes thread cache from main memory before the volatile load? for the source code for VolatileRead below, the arg_07_0 maybe already read the stale value from the local CPU memory, it doesn't make sense refresh the thread cache from main memory by Thread.MemoryBarrier() again; as arg_07_0 already read the stale value from local CPU cache.
    public  static object  VolatileRead(ref  object address)
     {
         object arg_07_0 = address;
         Thread.MemoryBarrier();
         return arg_07_0;
     }


    • Edited by Jacky_shen Friday, May 6, 2016 8:54 AM
    Friday, May 6, 2016 8:45 AM
  • "however from the articles below, we know volatile store caused the thread cache flushed to main memory after the operation"

    How do you know that the article is correct?

    "I think Thread.MemoryBarrier() maybe help to flush the value from the thread cache to main memory , please correct me If I am wrong."

    The documentation of MemoryBarrier doesn't say anything about flushing caches.

    This subject is a bit complicated unfortunately and the fact that people write misleading blog posts and that the MSDN documentation is as well misleading doesn't really help.

    Let's see what MSDN says about Thread.VolatileWrite:
    "On a multiprocessor system, VolatileWrite ensures that a value written to a memory location is immediately visible to all processors. This might require flushing processor caches.
    ...
    In C#, using the volatile modifier on a field guarantees that all access to that field uses VolatileRead or VolatileWrite."

    Now, let's see what the C# spec says about volatile since the documentation above refers to it:

    3.10 "Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects must be preserved are references to volatile fields (§10.5.3), lock statements (§8.12), and thread creation and termination."

    10.5.3 "A write of a volatile field is called a volatile write. A volatile write has “release semantics”; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence."

    And finally, let's look at the ECMA-335 spec to see what the volatile IL prefix is supposed to be doing, after all the C# compiler implements volatile by using this prefix.

    "A volatile write has “release semantics” meaning that the write is guaranteed to happen after any memory references prior to the write instruction in the CIL instruction sequence."

    The ECMA spec and C# spec are in agreement, they both use the acquire/release semantics and don't say anything about flushing caches. But the VolatileWrite documentation? Well, that does its own thing and then claims that C#'s volatile does the same thing even though it's obvious that the C# spec says otherwise.

    The problem is that this "immediately visible to all processors" is more or less nonsense and so is "flushing processor caches":

    • Things don't happen immediately in a multi processor system, there will always be some latency
    • Cache flushes do not imply ordering, it's possible that when the cache is flushed the CPU first writes the cacheline modified by the volatile store and then it writer cachelines modified by previous non-volatile stores. How useful would that be? Not very useful and cleary doesn't match with the release semantics required by the C# and ECMA specs.
    • Release semantics can be achieve without actually flushing any caches. All the CPU would need to do is to maintain a list of modified cachelines in the order they were modified. Actually writing those cachelines to memory can happen later, after the volatile store.
    • Requiring the cache to be flushed to ensure that other processors see the written value implies that the system doesn't maintain cache coherency. Many systems do not maintain instruction cache coherency but I don't know if there's a system that doesn't maintain data cache coherency. Such as sytem could exist in theory but in any case, x86/x64 systems do maintain data cache coherency.

    In the end the code you see in VolatileWrite and VolatileRead is the real deal. If the MSDN documentation or a random Internet blog post claim that those methods do something else than what the code obviously does then they are wrong and the code is correct.

    To make things even more complicated note that there's also a Volatile class that offers Read/Write methods with similar functionality. The code it uses is similar to the code used by Thread.VolatileWrite/Read but the Volatile class is treated specially by the JIT compiler, on x86 all the Read/Write methods are replaced with normal load/stores without any barriers. Not surprising given that x86/x64 memory model is more or less similar to the CLR memory model.
    Saturday, May 7, 2016 7:11 AM
    Moderator
  • "I think Thread.MemoryBarrier() maybe help to flush the value from the thread cache to main memory , please correct me If I am wrong."
    The documentation of MemoryBarrier doesn't say anything about flushing caches.

    This subject is a bit complicated unfortunately and the fact that people write misleading blog posts and that the MSDN documentation is as well misleading doesn't really help.

    Let's see what MSDN says about Thread.VolatileWrite:
    "On a multiprocessor system, VolatileWrite ensures that a value written to a memory location is immediately visible to all processors. This might require flushing processor caches.
    ...
    In C#, using the volatile modifier on a field guarantees that all access to that field uses VolatileRead or VolatileWrite."

    Stuff like if there is a Stack, wich Encoding is used internally for Strings or if there is string interning are a mater of Framework implementation.

    This is a mater of Framework, Driver and CPU/Mainboard implementation. It is way out of your ability to influence.
    If you really need to think and optimise on this level, .NET is just be the wrong tool for you.
    You need a much lower level langauge like BASIC.
    Or one that at least compiles directly to machine code (Native C++), rather then IL. IL code will be reinterpreted on the way to the CPU. Between deadcode detection and the JIT, you can never be 100% certain what will run on the CPU.

    The things you always have to trust are:
    Keyboard, Video, RAM, CPU, the Framework installation.
    Not because they are so reliable. But because there is nothing you can DO about it if they are broken. Them being broken causes Fatal Exceptions wich you should never try to catch.


    Saturday, May 7, 2016 11:08 AM
  • 1, For the VolatileWrite , why it call the Thread.MemoryBarrier(); before the assignment "address = value;"? it's ok for calling the Thread.MemoryBarrier(); after the assignment "address = value;"?

    The same question for VolatileRead, it's ok for calling the Thread.MemoryBarrier(); before the assignment "object arg_07_0 = address;"?

    [CLSCompliant(false)]
    [MethodImpl(MethodImplOptions.NoInlining)]
    publicstaticvoidVolatileWrite(refuint address, uintvalue)
    {
        Thread.MemoryBarrier();
        address = value;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static object VolatileRead(ref object address)
    {
        object arg_07_0 = address;
        Thread.MemoryBarrier();
        return arg_07_0;
    }

    2,From the URL below it says the Thread.MemoryBarrier Refreshes thread cache before the  operation and Flushes thread cache after operation ? what's the meaning? It means both the   _answer1 and   _answer2 would flush the thread cache to main memory After running the A() below?

      void A()
      {
        _answer1 = 1; 
        Thread.MemoryBarrier();      
         _answer2= 1; 
      }

    Construct Refreshes thread cache before? Flushes thread cache after? Notes
    Thread.MemoryBarrier Yes Yes Special memory barrier method

    http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/


    RJP's answers are correct. This subject is often confusing partly because the documentation is ambiguous in places or poorly phrased.

    For a detailed and thorough explanation I suggest you get a copy of this book:

    Saturday, May 7, 2016 5:23 PM
  • "Stuff like if there is a Stack, wich Encoding is used internally for Strings or if there is string interning are a mater of Framework implementation. ..."

    Your insistence in writing off-topic, ignorant and FUD filled posts is nothing short of amazing. The above post is in a league of its own.

    Tuesday, May 10, 2016 4:52 AM
    Moderator