独立于消耗原子负载的变量对操作有什么影响吗?
Is there any effect on the operations with the variables independent of consume atomic-load?
众所周知,有6个std::memory_order's,其中2个:
- 用于加载的获取语义 - 避免重新排序 Load-Load 和 Load-Store
- release-semantic 用于存储 - 避免重新排序 Store-Store 和 Load-Store
即对于acquire-semantic,在X.load(std::memory_order_acquire);
:
之后只能执行S = local1;
static std::atomic<int> X;
static int L, S;
...
void thread_func()
{
int local1 = L; // load(L)-load(X) - !!! can be reordered with X !!!
S = local1; // store(S)-load(X) - !!! can be reordered with X !!!
int x_local = X.load(std::memory_order_acquire); // load(X)
int local2 = L; // load(X)-load(L) - can't be reordered with X
S = local2; // load(X)-store(S) - can't be reordered with X
}
但是 load(X)
中哪些重新排序可以用于语义消费?
static std::atomic<int *> X;
static int L1, L2, S1, S2;
static int L, S;
...
void thread_func()
{
int *x_ptr_local = new int(1);
int local1 = L1; // load(L1)-load(X) - !!! can be reordered with X !!!
S1 = local1; // store(S1)-load(X) - !!! can be reordered with X !!!
int dependent_x1 = *x_ptr_local; // load(x_ptr)-load(X) - !!! can be reordered with X !!!
S = dependent_x1; // store(S)-load(X) - !!! can be reordered with X !!!
x_ptr_local = X.load(std::memory_order_consume); // load(X)
int dependent_x2 = *x_ptr_local; // load(X)-load(x_ptr) - can't be reordered with X
S = dependent_x2; // load(X)-store(S) - can't be reordered with X
int local2 = L2; // load(X)-load(L2) - !!! can be reordered with X !!!
S2 = local2; // load(X)-store(S2) - !!! can be reordered with X !!!
}
真的吗,只有 dependent_x2
的操作不能跨 X.load(std::memory_order_consume)
重新排序?
并且所有具有变量 L1
、L2
、S1
、S2
和 dependent_x1
的操作都可以在 X.load(std::memory_order_consume)
中重新排序 -即可以在 X.load(std::memory_order_consume)
之前或之后执行,不是吗?
memory_order_consume
用于保留数据依赖于原子对象本身的顺序,而不使用更重的同步,例如 memory_order_acquire
引入的同步。对于 memory_order_acquire
,all 内存操作在 acquire
之后——取决于原子变量或其他——禁止在它之前重新排序,而 memory_order_consume
仅禁止重新排序 dependent 指令。这有利于更弱排序的架构,例如 ARM 和 PowerPC,它们可以保证数据相关指令的排序,而无需显式屏障。
由于 memory_order_consume
处理数据依赖性,因此它的大多数用例都涉及 std::atomic<T*>
。生产者线程可以构建一个完整的数据结构,并使用 memory_order_release
将该数据结构的地址发布到原子指针。然后,消费者线程使用 memory_order_consume
加载原子指针,并且如果它们以数据相关的方式使用指针(例如取消引用),则可以与写入线程的存储建立数据依赖关系。该标准保证任何依赖加载都将反映编写器线程的存储。然而,由于原子变量的加载是通过 memory_order_consume
完成的,因此无法保证从 reader 线程的角度来看自变量的状态。
在您的第一个示例中,memory_order_acquire
之后的负载中的 none 可以在它之前重新排序。但是,在您的第二个示例中,任何不依赖 X
或其加载值的重新排序都是公平的。即,int dependent_x2 = *x_ptr_local;
(以及来自 dependent_x2
的相应负载)保证相对于 X
保持有序,仅此而已。所有其他重新排序都是可能的。
众所周知,有6个std::memory_order's,其中2个:
- 用于加载的获取语义 - 避免重新排序 Load-Load 和 Load-Store
- release-semantic 用于存储 - 避免重新排序 Store-Store 和 Load-Store
即对于acquire-semantic,在X.load(std::memory_order_acquire);
:
S = local1;
static std::atomic<int> X;
static int L, S;
...
void thread_func()
{
int local1 = L; // load(L)-load(X) - !!! can be reordered with X !!!
S = local1; // store(S)-load(X) - !!! can be reordered with X !!!
int x_local = X.load(std::memory_order_acquire); // load(X)
int local2 = L; // load(X)-load(L) - can't be reordered with X
S = local2; // load(X)-store(S) - can't be reordered with X
}
但是 load(X)
中哪些重新排序可以用于语义消费?
static std::atomic<int *> X;
static int L1, L2, S1, S2;
static int L, S;
...
void thread_func()
{
int *x_ptr_local = new int(1);
int local1 = L1; // load(L1)-load(X) - !!! can be reordered with X !!!
S1 = local1; // store(S1)-load(X) - !!! can be reordered with X !!!
int dependent_x1 = *x_ptr_local; // load(x_ptr)-load(X) - !!! can be reordered with X !!!
S = dependent_x1; // store(S)-load(X) - !!! can be reordered with X !!!
x_ptr_local = X.load(std::memory_order_consume); // load(X)
int dependent_x2 = *x_ptr_local; // load(X)-load(x_ptr) - can't be reordered with X
S = dependent_x2; // load(X)-store(S) - can't be reordered with X
int local2 = L2; // load(X)-load(L2) - !!! can be reordered with X !!!
S2 = local2; // load(X)-store(S2) - !!! can be reordered with X !!!
}
真的吗,只有 dependent_x2
的操作不能跨 X.load(std::memory_order_consume)
重新排序?
并且所有具有变量 L1
、L2
、S1
、S2
和 dependent_x1
的操作都可以在 X.load(std::memory_order_consume)
中重新排序 -即可以在 X.load(std::memory_order_consume)
之前或之后执行,不是吗?
memory_order_consume
用于保留数据依赖于原子对象本身的顺序,而不使用更重的同步,例如 memory_order_acquire
引入的同步。对于 memory_order_acquire
,all 内存操作在 acquire
之后——取决于原子变量或其他——禁止在它之前重新排序,而 memory_order_consume
仅禁止重新排序 dependent 指令。这有利于更弱排序的架构,例如 ARM 和 PowerPC,它们可以保证数据相关指令的排序,而无需显式屏障。
由于 memory_order_consume
处理数据依赖性,因此它的大多数用例都涉及 std::atomic<T*>
。生产者线程可以构建一个完整的数据结构,并使用 memory_order_release
将该数据结构的地址发布到原子指针。然后,消费者线程使用 memory_order_consume
加载原子指针,并且如果它们以数据相关的方式使用指针(例如取消引用),则可以与写入线程的存储建立数据依赖关系。该标准保证任何依赖加载都将反映编写器线程的存储。然而,由于原子变量的加载是通过 memory_order_consume
完成的,因此无法保证从 reader 线程的角度来看自变量的状态。
在您的第一个示例中,memory_order_acquire
之后的负载中的 none 可以在它之前重新排序。但是,在您的第二个示例中,任何不依赖 X
或其加载值的重新排序都是公平的。即,int dependent_x2 = *x_ptr_local;
(以及来自 dependent_x2
的相应负载)保证相对于 X
保持有序,仅此而已。所有其他重新排序都是可能的。