c++ - C++11 中的内存排序与主内存刷新排序有关吗?

标签 c++ atomic memory-barriers memory-model stdatomic

我不确定我是否完全理解(并且我可能全错)C++11 中原子性和内存排序的概念。 让我们以这个简单的单线程示例为例:

int main()
{
    std::atomic<int> a(0);
    std::atomic<int> b(0);
    a.store(16);
    b.store(10);

    return 0;
}

在这个单线程代码中,如果 a 和 b 不是原子类型,编译器可能会以在汇编代码中的方式重新排序指令,例如,我有一条移动指令,在 a 之前将 10 分配给“b”将指令移动到指定的 16 到“a”。 因此,对我来说,作为原子变量,它保证我在“b 移动指令”之前有“a 移动指令”,正如我在源代码中所述。 之后是处理器及其执行单元、预取指令和乱序盒。并且该处理器可以在“a 指令”之前处理“b 指令”,无论汇编代码中的指令顺序如何。 因此,在将 16 个存储在寄存器/存储缓冲区或高速缓存中之前,我可以将 10 个存储在寄存器或处理器的存储缓冲区或高速缓存中。

根据我的理解,这就是内存排序模型的诞生。从那一刻起,如果我让默认模型顺序一致。有人向我保证,在主内存中清除这些值(10 和 16)将遵循我在源代码中存储的顺序。这样处理器将开始刷新寄存器或缓存,其中 16 存储到主内存中用于更新“a”,然后它将刷新主内存中的 10 用于“b”。

所以这种行为确实让我明白,如果我使用宽松的内存模型。只有最后一部分不保证,因此主内存中的刷新可能会完全困惑。

抱歉,如果您阅读我的内容遇到麻烦,我的英语仍然很差。但谢谢你们抽出时间。

最佳答案

C++ 内存模型是关于抽象机器和值可见性,而不是关于“主内存”、“写入队列”或“刷新”等具体事物。

在您的示例中,内存模型指出,由于对 a 的写入发生在对 b 的写入之前,因此任何从 b 读取 10 的线程 在随后从 a 读取时必须看到 16(当然,除非此后已被覆盖)。

这里重要的是建立发生之前的关系和值(value)可见性。如何映射到缓存和内存取决于编译器。在我看来,最好停留在抽象级别上,而不是尝试将模型映射到您对硬件的理解,因为

  • 您对硬件的理解可能是错误的。硬件比 C++ 内存模型还要复杂。
  • 即使您现在的理解是正确的,更高版本的硬件也可能有不同的模型,至少在子系统中是这样。
  • 通过映射到硬件模型,您可能会对不同硬件模型的影响做出错误的假设。例如。如果您了解内存模型如何映射到 x86 硬件,您将无法理解 PowerPC 上消耗和获取之间的细微差别。
  • C++ 模型非常适合推理正确性。

关于c++ - C++11 中的内存排序与主内存刷新排序有关吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29669700/

相关文章:

c++ - QT信号和槽函数签名

c++ - 为双向迭代器提供operator+或operator-有什么缺点吗?

c++ - CUDA AtomicCAS 死锁

c - 内存屏障和缓存刷新

c++ - 为什么 fetch_sub 不是释放操作?

c++ - 在 ARM 上加载和存储重新排序

C++ - 简单 SharedQueue 是否需要互斥量?

c++ - 新建/删除运算符重载和基类

c - 为什么我的消费者线程在我的生产者线程完成之前就停止了?

c++11 - 多个线程是否可以使用 std::atomic 使用 memory_order_acquire 将负载与单个存储同步