What does the [[carry_dependency]] attribute mean?

Can anyone explain this in a language that ordinary mortals understand?

+60
c ++ multithreading c ++ 11 memory-model carries-dependency
Jun 20 2018-11-12T00:
source share
2 answers

[[carries_dependency]] used to allow dependencies to be carried through function calls. This potentially allows the compiler to generate better code when used with std::memory_order_consume to pass values ​​between threads on platforms with poorly ordered architectures such as the IBM POWER architecture.

In particular, if a value read using memory_order_consume is passed to a function, then without [[carries_dependency]] , the compiler may have to issue a memory save command to ensure that the appropriate semantics of memory ordering are maintained. If the parameter is annotated using [[carries_dependency]] , then the compiler may assume that the function body will correctly transport the dependency, and this fence will no longer be needed.

Similarly, if a function returns a value loaded with memory_order_consume or obtained from that value, then without [[carries_dependency]] compiler may need to insert a guard command to ensure that the appropriate semantics of memory ordering are maintained. With the annotation [[carries_dependency]] this fence may not be needed, since the caller is responsible for maintaining the dependency tree.

eg.

 void print(int * val) { std::cout<<*p<<std::endl; } void print2(int * [[carries_dependency]] val) { std::cout<<*p<<std::endl; } std::atomic<int*> p; int* local=p.load(std::memory_order_consume); if(local) std::cout<<*local<<std::endl; // 1 if(local) print(local); // 2 if(local) print2(local); // 3 

In line (1), the dependency is explicit, so the compiler knows that local dereferenced and that it must ensure that the dependency chain is preserved in order to avoid being protected from POWER.

On line (2), the definition of print opaque (unless specified), so the compiler must throw a fence to make sure that reading *p in print returns the correct value.

On line (3), the compiler can assume that although print2 also opaque, then the dependency on the parameter to the dereferenced value is stored in the command stream, and no fence is required in POWER. Obviously, the definition of print2 should actually preserve this dependency, so the attribute will also affect the generated code for print2 .

+48
Jun 20 '11 at 13:14
source share

In short, if there is an attribute carry_dependency, the generated code for the function should be optimized for the case when the actual argument will actually come from another thread and carries a dependency. Similarly for the return value. There may be a performance penalty if this assumption is incorrect (for example, in a single-threaded program). But also the lack of [[carry_dependency]] can lead to poor performance in the opposite case ... No other effects, other than changes in performance, should happen.

For example, the dereferencing operation of a pointer depends on how the pointer was previously received, and if the value of the pointer p comes from another thread (through the "consume" operation), the value previously assigned by this other thread to * p is taken into account and visible. There may be another pointer q, which is equal to p (q == p), but since its value does not come from this other stream, the value * q can be considered as different from * p. In fact, * q can provoke a peculiar undefined behavior (since the location of the access memory is not consistent with the other thread that performed the assignment).

Indeed, it seems that in the functional capabilities of memory (and mind) there are some big mistakes in certain technical cases ....> :-)

-2
Jan 22 '14 at 3:48
source share



All Articles