官术网_书友最值得收藏!

Memory model and compiler optimizations

Memory model and compiler optimizations are not directly related to concurrency, but they are very important concepts for anyone who creates concurrent code, shown as follows:

class Program
{
  bool _loop = true;

  static void Main(string[] args)
  {
    var p = new Program();

    Task.Run(() =>
    {
      Thread.Sleep(100);
      p._loop = false;
    });

    while (p._loop);
    //while (p._loop) { Console.Write(".");};

    Console.WriteLine("Exited the loop");
  }
}

If you compile this with the Release build configuration and JIT compiler optimizations enabled, the loop will usually hang on the x86 and x64 architectures. This happens because JIT optimizes the p._loop read and does something like this:

if(p._loop)
{
  while(true);
}

If there is something inside the while loop, JIT will probably not optimize this code in this way. Also, we may use the volatile keyword with the Boolean flag like this:

volatile bool _loop;

In this case, JIT will turn off this optimization as well. This is where we use a memory model, and it gets complicated here. Here is a quote from the C# language specification:

For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock-statement. These optimizations can be performed by the compiler, by the run-time system, or by hardware. For volatile fields, such reordering optimizations are restricted:

  • A read of a volatile field is called a volatile read. A volatile read has "acquire semantics"; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.
  • 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.

As we can see, there is nothing specifically stated here about compiler optimizations, but in fact JIT does not optimize volatile field read in this case.

So we can see a description in a specification, but how does this really work? Let's look at a volatile read example:

class VolatileRead
{
  int _x;
  volatile int _y;
  int _z;

  void Read()
  {
    int x = _x; // 1
    int y = _y; // 2 (volatile)
    int z = _z; // 3
  }
}

The possible reordering options would be 1, 2, 3 (original); 2, 1, 3; and 2, 3, 1. This can be imagined as a one-way fence that allows the preceding operation to pass through, but does not allow subsequent operations. So this is called the acquire fence.

Volatile writes look pretty similar. Consider the following code snippet:

class VolatileWrite
{
  int _x;
  volatile int _y;
  int _z;

  void Read()
  {
    _x = 1; // 1
    _y = 2; // 2 (volatile)
    _z = 3; // 3
  }
}

Possible options here are 1, 2, 3 (original); 1, 3, 2; and 3, 1, 2. This is the release fence, which allows the reordering of only subsequent read or write operations but does not allow the preceding write operation. We have the Thread.VolatileRead and Thread.VolatileWrite methods that do the same thing explicitly. There is the Thread.MemoryBarrier (memory barrier) method as well, which allows us to use a full fence when we do not let through any operations.

I would like to mention that we are now on less certain ground. Different memory models on different architectures can be confusing, and code without volatile can perfectly work on x86 and amd64. However, if you are using shared data, please be aware of possible reordering and non-reordering optimizations and choose the appropriate behavior.

Note

Please be aware that making a field volatile means that all the read and write operations will have slightly lower performance and they will have the code in common, since some possible optimizations will be ignored.

主站蜘蛛池模板: 双流县| 偃师市| 临猗县| 靖边县| 淅川县| 合肥市| 通渭县| 金湖县| 祁连县| 进贤县| 泸西县| 盱眙县| 澄城县| 麟游县| 霍林郭勒市| 呼伦贝尔市| 喀什市| 宜兴市| 丽江市| 蕉岭县| 施秉县| 镇江市| 攀枝花市| 白银市| 景洪市| 内江市| 郯城县| 嘉荫县| 弥勒县| 通山县| 伊金霍洛旗| 克拉玛依市| 清水河县| 绥德县| 滁州市| 兰坪| 龙山县| 宁津县| 永宁县| 塘沽区| 昌邑市|