Difference between revisions of "cpp/atomic/kill dependency"
From cppreference.com
m (fmt) |
m (→Example: Any point to use elaborated type spec here?.) |
||
Line 1: | Line 1: | ||
− | {{cpp/title|kill_dependency }} | + | {{cpp/title|kill_dependency}} |
{{cpp/thread/navbar}} | {{cpp/thread/navbar}} | ||
− | {{ | + | {{ddcl|header=atomic|since=c++11|1= |
− | + | ||
− | + | ||
template< class T > | template< class T > | ||
T kill_dependency( T y ) noexcept; | T kill_dependency( T y ) noexcept; | ||
}} | }} | ||
− | |||
Informs the compiler that the dependency tree started by an {{lc|std::memory_order_consume}} atomic load operation does not extend past the return value of {{tt|std::kill_dependency}}; that is, the argument does not carry a dependency into the return value. | Informs the compiler that the dependency tree started by an {{lc|std::memory_order_consume}} atomic load operation does not extend past the return value of {{tt|std::kill_dependency}}; that is, the argument does not carry a dependency into the return value. | ||
Line 19: | Line 16: | ||
===Return value=== | ===Return value=== | ||
− | Returns {{ | + | Returns {{c|y}}, no longer a part of a dependency tree. |
===Examples=== | ===Examples=== | ||
=====file1.cpp:===== | =====file1.cpp:===== | ||
{{source|1= | {{source|1= | ||
− | struct | + | struct Foo |
− | std::atomic< | + | { |
+ | int* a; | ||
+ | int* b; | ||
+ | }; | ||
+ | |||
+ | std::atomic<Foo*> foo_head[10]; | ||
int foo_array[10][10]; | int foo_array[10][10]; | ||
// consume operation starts a dependency chain, which escapes this function | // consume operation starts a dependency chain, which escapes this function | ||
− | [[carries_dependency]] | + | [[carries_dependency]] Foo* f(int i) |
+ | { | ||
return foo_head[i].load(memory_order_consume); | return foo_head[i].load(memory_order_consume); | ||
} | } | ||
− | // the dependency chain enters this function through the right parameter | + | // the dependency chain enters this function through the right parameter and is |
− | // | + | // killed before the function ends (so no extra acquire operation takes place) |
− | int g(int* x, int* y [[carries_dependency]]) { | + | int g(int* x, int* y [[carries_dependency]]) |
+ | { | ||
return std::kill_dependency(foo_array[*x][*y]); | return std::kill_dependency(foo_array[*x][*y]); | ||
} | } | ||
Line 41: | Line 45: | ||
=====file2.cpp:===== | =====file2.cpp:===== | ||
{{source|1= | {{source|1= | ||
− | [[carries_dependency]] | + | [[carries_dependency]] Foo* f(int i); |
int g(int* x, int* y [[carries_dependency]]); | int g(int* x, int* y [[carries_dependency]]); | ||
int c = 3; | int c = 3; | ||
− | void h(int i) { | + | void h(int i) |
− | struct | + | { |
+ | struct Foo* p; | ||
p = f(i); // dependency chain started inside f continues into p without undue acquire | p = f(i); // dependency chain started inside f continues into p without undue acquire | ||
do_something_with(g(&c, p->a)); // p->b is not brought in from the cache | do_something_with(g(&c, p->a)); // p->b is not brought in from the cache |
Revision as of 08:56, 26 July 2023
Defined in header <atomic>
|
||
template< class T > T kill_dependency( T y ) noexcept; |
(since C++11) | |
Informs the compiler that the dependency tree started by an std::memory_order_consume atomic load operation does not extend past the return value of std::kill_dependency
; that is, the argument does not carry a dependency into the return value.
This may be used to avoid unnecessary std::memory_order_acquire fences when the dependency chain leaves function scope (and the function does not have the [[carries_dependency]]
attribute).
Contents |
Parameters
y | - | the expression whose return value is to be removed from a dependency tree |
Return value
Returns y, no longer a part of a dependency tree.
Examples
file1.cpp:
struct Foo { int* a; int* b; }; std::atomic<Foo*> foo_head[10]; int foo_array[10][10]; // consume operation starts a dependency chain, which escapes this function [[carries_dependency]] Foo* f(int i) { return foo_head[i].load(memory_order_consume); } // the dependency chain enters this function through the right parameter and is // killed before the function ends (so no extra acquire operation takes place) int g(int* x, int* y [[carries_dependency]]) { return std::kill_dependency(foo_array[*x][*y]); }
file2.cpp:
[[carries_dependency]] Foo* f(int i); int g(int* x, int* y [[carries_dependency]]); int c = 3; void h(int i) { struct Foo* p; p = f(i); // dependency chain started inside f continues into p without undue acquire do_something_with(g(&c, p->a)); // p->b is not brought in from the cache do_something_with(g(p->a, &c)); // left argument does not have the carries_dependency // attribute: memory acquire fence may be issued // p->b becomes visible before g() is entered }
See also
(C++11) |
defines memory ordering constraints for the given atomic operation (enum) |
C documentation for kill_dependency
|