Difference between revisions of "cpp/atomic/kill dependency"
From cppreference.com
m (use attr) |
|||
(11 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
− | {{cpp/title | kill_dependency }} | + | {{cpp/title|kill_dependency}} |
− | {{cpp/ | + | {{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 {{ | + | 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. |
− | This may be used to avoid unnecessary {{lc|std::memory_order_acquire}} fences when the dependency chain leaves function scope (and the function does not have the {{attr|carries_dependency}} attribute) | + | This may be used to avoid unnecessary {{lc|std::memory_order_acquire}} fences when the dependency chain leaves function scope (and the function does not have the {{attr|carries_dependency}} attribute). |
===Parameters=== | ===Parameters=== | ||
{{par begin}} | {{par begin}} | ||
− | {{par | y | the expression whose return value is to be removed from a dependency tree}} | + | {{par|y|the expression whose return value is to be removed from a dependency tree}} |
− | {{par end}} | + | {{par end}} |
===Return value=== | ===Return value=== | ||
− | Returns {{ | + | Returns {{c|y}}, no longer a part of a dependency tree. |
− | + | ||
− | + | ||
===Examples=== | ===Examples=== | ||
+ | =====file1.cpp:===== | ||
{{source|1= | {{source|1= | ||
− | + | struct Foo | |
− | struct | + | { |
− | 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]); | ||
} | } | ||
}} | }} | ||
− | + | =====file2.cpp:===== | |
{{source|1= | {{source|1= | ||
− | + | [[carries_dependency]] struct Foo* f(int i); | |
− | [[carries_dependency]] struct | + | |
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) |
− | + | { | |
+ | 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 | ||
Line 59: | Line 61: | ||
===See also=== | ===See also=== | ||
− | |||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/atomic/dsc memory_order}} | + | {{dsc inc|cpp/atomic/dsc memory_order}} |
+ | {{dsc see c|c/atomic/kill_dependency}} | ||
{{dsc end}} | {{dsc end}} | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 05:52, 27 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 |
[edit] Parameters
y | - | the expression whose return value is to be removed from a dependency tree |
[edit] Return value
Returns y, no longer a part of a dependency tree.
[edit] Examples
[edit] 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]); }
[edit] file2.cpp:
[[carries_dependency]] struct Foo* f(int i); int g(int* x, int* y [[carries_dependency]]); int c = 3; void h(int i) { 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 }
[edit] See also
(C++11) |
defines memory ordering constraints for the given atomic operation (enum) |
C documentation for kill_dependency
|