C++11 Memory Model Interpretation

Source: Internet
Author: User

C++11 Memory Model Interpretation About Disorderly Order

When it comes to memory models, the first thing you need to do is to identify a ubiquitous, but not everyone, fact: The program is not always executed according to the order of the source code one by one, this is called the chaos sequence, the cause of the chaos may be several kinds:

    1. For optimization purposes, the compiler exchanges the order of the source code during the compilation phase.
    2. During program execution, the instruction pipelining is executed by the CPU.
    3. The layering and refresh strategy of the inherent cache makes it possible to rearrange the order of some read operations from the effect.

Although the above-mentioned disorder source is different, but from the source point of view, for the upper-level applications, their effect is actually the same: the code written out and the last executed code is inconsistent. This fact can be surprising: how can you write the correct code if you have such a serious problem? This worry is superfluous, although the phenomenon of disorder is ubiquitous, but they all have a very important thing in common: in the case of single-threaded execution, the execution of the sequence and not the order, the final result will be the same (both end up with the same observable result), This is the first principle to be followed in order to be allowed to appear, and the root cause of why the disorder is always present but most programmers do not feel most of the time.

The emergence of Chaos is the compiler, CPU, etc. in order to make your program run faster and make unlimited efforts to the results, programmers should be their good intentions to wipe a tear.

From the sort of disorderly order, the chaos can be divided into the following 4 kinds:

    1. Write out of Order (store), the previous write operation is placed behind the operation, such as:

      3;b = 4;被乱序为:b = 4;a = 3;
    2. Write out the order (store load), the previous write operation is placed behind the read operation, such as:

      3;load(b);被乱序为load(b);a = 3;
    3. Read the random order (load load), the preceding read operation is placed after the next read operation, such as:

      load(a);load(b);被乱序为:load(b);load(a);
    4. Read-Write Chaos (Load store), the preceding reading is placed after the next write operation, such as:

      4;被乱序为:b = 4;load(a);

The chaos of the program in the single-threaded world most of the time did not cause much attention to the problem, but in the multi-threaded world, these disorderly order to create a special trouble, the reason, the main 2:

    1. Concurrency is not guaranteed to modify and access the operational atomicity of shared variables, so that some intermediate states are exposed, so that things like mutexes and various lock types are frequently used when writing multiple threads.
    2. When a variable is modified, the modification may not be observed by another thread in time, so it needs to be "synchronized".

Resolving synchronization issues requires determining the memory model, which is how you need to determine how the threads should interact with shared memory (see Wikipedia).

Memory Model

What the memory model wants to express is mainly about how to describe the effect of a memory operation on the visibility of each thread. The effect of the modification operation can not be seen in time by other threads, there are many reasons, the more obvious one is, for the computer, usually the memory of the write operation is much more expensive than the read operation is many, so the optimization of the write operation is the key to improve performance, and these write operations of various optimizations, Results in a common phenomenon: write operations are usually cached in the cache inside the CPU. This results in a CPU in a write operation, the operation caused by the memory changes are not necessarily immediately seen by the other CPU, this from another perspective, the effect is actually read and write chaos.

3;cpu2 执行如下:load(a);

For the above code, suppose that the initial value of a is 0, then CPU1 executes first, then CPU2 executes, assuming that the read and write are atomic, then the last cpu2 if read a = 0 is actually not a strange thing. Obviously, this kind of thread successfully modifies the global variables in a certain thread, and the consequences of not seeing the effect on the other threads are very serious.

Therefore, it is necessary to have the means to synchronize the behavior of modifying public variables.

The following 6 semantics are defined in the Atomic library in c++11 to contract the behavior of memory operations, which specify the visibility of different memory operations in other threads, respectively:

enum memory_order {    memory_order_relaxed,    memory_order_consume,    memory_order_acquire,    memory_order_release,    memory_order_acq_rel,    memory_order_seq_cst};

We mainly discuss several of them: relaxed, acquire, release, SEQ_CST (sequential consistency).

Relaxed Semantics

The first is the relaxed semantics, which represents one of the most relaxed memory operation conventions, the Convention is not to make a contract, in this way to modify memory, do not need to ensure that the changes will be in time to be seen by other threads, and do not make any order of chaos, so when the public variables in relaxed way to read and write, Compilers, CPUs, etc. are allowed to be optimized in any way they deem appropriate.

Release-acquire Semantics

If you have ever seen other articles about memory models, you will find that it is not accidental that release is always put together with acquire. In fact, release and acquire are complementary, they must be used together, these two are a "package deal", separate use is completely meaningless. Specifically, release is used for write operations, and acquire is used for read operations, which together represent such a convention:

If a thread a modifies a piece of memory m with release, then in thread A, all memory operations made prior to the release operation become visible after another thread B reads memory m in acquire mode.

To give a chestnut, assume that thread A executes the following command:

a.store(3);b.store(4);m.store(5, release);

Thread B executes as follows:

e.load();f.load();m.load(acquire);g.load();h.load();

As above, suppose that thread a executes first, thread B executes, because thread a modifies m in release, and Thread B reads m in acquire, so when thread B finishes executing m.load(acquire) , thread B must already be able to see . The above-mentioned rigid descriptions actually convey additional, less obvious information:

    • Release and acquire are relative to two threads, and it is agreed that the relative behavior of the two thread: if one thread a modifies the public variable m in the way of release, and the other thread B reads the m in a acquire way, it does not Ensure that there is a consequence if there is another thread C that reads m in a non-acquire way.

    • A certain degree prevents the occurrence of the disorder, because all operations before the release operation are required to be visible after the other thread acquire, then:
      • All memory operations before the release operation are not allowed to be scrambled to release.
      • All memory operations after the acquire operation are not allowed to be ordered before acquire.

And in terms of their use, there are a few points that need to be noted and emphasized in particular:

    1. Release and acquire must be used together, separate use is meaningless.
    2. Release is only valid for write operations (store) and is meaningless for read (load).
    3. Acquire is only valid for read operations and is meaningless for write operations.

Modern processors often support directives such as Read-modify-write, which, for this kind of instruction, sometimes we might want to perform a release on the operation and perform acquire on the operation, so c++11 also defines memory_order_acq_ Rel, this type of operation is the combination of release and acquire, in addition to the aforementioned role, but also played a memory barrier function.

Sequential Consistency

Sequential consistency is equivalent to release + acquire, plus a requirement to add a global order to the operation, what does that mean?

Simply put, for all memory operations that are performed in MEMORY_ORDER_SEQ_CST, regardless of whether they are scattered across different CPUs, the effects of these operations ultimately require a global order, and this order appears consistent across the relevant threads.

For a chestnut, assume that the initial value of a, B is 0:

Thread A executes:

a.store(3, seq_cst);

Thread B executes:

b.store(4, seq_cst);

If the modification of A and B is performed simultaneously on two lines thread, but these actions are non-atomic, the operations must have a sequence in the global order:

    1. First modify A, then modify B, or
    2. First modify the B and put the whole A.

And this order is fixed, it must be the same on any other thread, so a = = 0 && b = = 4 and a = = 3 && b = = 0 are not allowed to be set up at the same time.

something

This essay has been lying in my draft case for more than half a year, more than half a year I constantly tidy up in this area of knowledge, but also constantly clarify their own ideas, and finally still feel about the memory model there are too many can say but not all of a sudden can be said clearly things, so here can only say things to reduce and reduce, the scope to c+ +11 a simple introduction to the language level, purely as a summary, interested in in-depth understanding of more details of the reader, I strongly recommend to see the Herb Sutter in this area to do a talk, memory model knowledge is difficult to understand, more difficult to use the correct, In most cases use it to get a few performance advantages, has been completely unworthy of the code complexity and readability of the loss, if you are still hesitant to use these relatively low-level things, do not use it, hesitate to indicate that there are other options, no choice, do not personally achieve the lock Something related to free.

Reference

http://bartoszmilewski.com/2008/11/11/who-ordered-sequential-consistency/

http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/

http://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/

Http://en.cppreference.com/w/cpp/atomic/memory_order

Http://preshing.com

C++11 Memory Model Interpretation

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.