13

在这篇文章中:http ://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2 说,我们不能对 进行任何优化volatile,即使是(其中:)volatile int& v = *(address);

v = 1;                // C: write to v
local = v;            // D: read from v

无法对此进行优化:

v = 1;                // C: write to v
local = 1;            // D: read from v  // but it can be done for std::atomic<>

这是无法完成的,因为第一行和第二行之间的值可能会被硬件设备(不是不能工作缓存一致性的 CPU:网络适配器、GPU、FPGA 等)v更改(顺序/并发),映射到这个内存位置。但只有当不能缓存在 CPU 缓存 L1/2/3 中时才有意义,因为对于第 1 行和第 2 行之间的通常(非)变量来说,时间太短并且很可能触发缓存。vvolatile

限定符是否volatile保证不缓存此内存位置?

回答:

  1. 不,volatile 不保证不会缓存此内存位置,并且在 C/C++ 标准或编译器手册中没有任何关于此的内容。
  2. 使用内存映射区域,当从设备内存映射到 CPU 内存的内存已经标记为 WC(写入组合)而不是 WB 时,将取消缓存。并且不需要做缓存刷新
  3. 相反,如果 CPU 内存映射到设备内存,那么顺便提一下,位于 CPU 晶体上的控制器 PCIE 正在窥探从该设备通过 DMA 的数据,并更新(无效)CPU 缓存 L3。在这种情况下,如果设备上的可执行代码 volatile尝试执行相同的两行,它也会取消设备的缓存(例如在缓存 GPU-L2 中)。并且不需要做 GPU-cache-flushing 也不需要做 CPU-cache-flushing。如果 L3-cache(LLC) 与 DMA over PCIE 的一致性, CPU 也可能需要使用std::atomic_thread_fence(std::memory_order_seq_cst); ,但 L1/L2 不是。对于 nVidia CUDA,我们可以使用:void __threadfence_system();
  4. 当发送未对齐的数据时,我们需要刷新 DMA-controllers-cache : (WDK: KeFlushIoBuffers(), FlushAdapterBuffers())
  5. 此外,我们可以通过 MTRR 寄存器将任何内存区域标记为您自己标记为 WC 的未缓存。
4

2 回答 2

8

volatile确保变量不会被“缓存”在 CPU 寄存器中。CPU 缓存对程序员是透明的,如果另一个 CPU 写入另一个 CPU 的缓存映射的内存,第二个 CPU 的缓存就会失效,因此它会在下一次访问时再次从内存中重新加载值。

关于缓存一致性的一些事情

至于外部存储器写入(通过 DMA 或其他与 CPU 无关的通道),您可能需要手动刷新缓存(请参阅SO 问题)


C标准§6.7.3 7:

构成对具有 volatile 限定类型的对象的访问是实现定义的。

于 2013-08-31T17:21:39.003 回答
0

volatile 的语义是实现定义的。如果编译器知道在执行某些代码时中断将被禁用,并且知道在目标平台上除了中断处理程序之外没有其他方法可以观察到某些存储上的操作,它可以注册缓存 volatile-如果它记录了这种行为,那么这种存储中的限定变量就像它可以缓存普通变量一样。

请注意,行为的哪些方面被视为“可观察的”可以由实现以某种方式定义。如果一个实现证明它不打算在使用主 RAM 访问来触发所需的外部可见操作的硬件上使用,那么在该实现上对主 RAM 的访问将不是“可观察的”。如果没有人关心是否实际看到任何此类访问,则该实现将与能够物理观察此类访问的硬件兼容。但是,如果需要这样的访问,就像这些访问被认为是“可观察的”一样,编译器不会声称兼容性,因此不会对任何事情做出任何承诺。

于 2018-09-22T06:52:45.270 回答