在这篇文章中: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 行之间的通常(非)变量来说,时间太短并且很可能触发缓存。v
volatile
限定符是否volatile
保证不缓存此内存位置?
回答:
- 不,
volatile
不保证不会缓存此内存位置,并且在 C/C++ 标准或编译器手册中没有任何关于此的内容。 - 使用内存映射区域,当从设备内存映射到 CPU 内存的内存已经标记为 WC(写入组合)而不是 WB 时,将取消缓存。并且不需要做缓存刷新。
- 相反,如果 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();
- 当发送未对齐的数据时,我们需要刷新 DMA-controllers-cache : (WDK:
KeFlushIoBuffers(), FlushAdapterBuffers()
) - 此外,我们可以通过 MTRR 寄存器将任何内存区域标记为您自己标记为 WC 的未缓存。