记忆栅栏是如何工作的?

Posted

技术标签:

【中文标题】记忆栅栏是如何工作的?【英文标题】:How do memory fences work? 【发布时间】:2011-11-08 22:56:55 【问题描述】:

我需要了解多核机器中的内存栅栏。说我有这个代码

核心 1

mov [_x], 1; mov r1, [_y]    

核心 2

mov [_y], 1; mov r2, [_x]

现在没有内存栅栏的意外结果是执行后 r1 和 r2 都可以为 0。在我看来,为了解决这个问题,我们应该在两个代码中都放置内存栅栏,因为只放置一个仍然不能解决问题。如下所示...

核心 1

mov [_x], 1; memory_fence; mov r1, [_y]  

核心 2

mov [_y], 1; memory_fence; mov r2, [_x]

我的理解正确还是我仍然遗漏了什么?假设架构是 x86。另外,谁能告诉我如何在 C++ 代码中设置内存栅栏?

【问题讨论】:

恐怕不容易一概而论;它非常依赖于处理器的内存模型和内存栅栏指令的功能。您是否有特别感兴趣的特定处理器? 【参考方案1】:

C++11 (ISO/IEC 14882:2011) 定义了一个多线程感知内存模型。 虽然我不知道目前有任何编译器实现了新的内存模型,C++ Concurrency in Action by Anthony Williams 很好地记录了它。你可以查看Chapter 5 - The C++ Memory Model and Operations on Atomic Types,他解释了宽松的操作和内存栅栏。此外,他还是 just::thread 库的作者,该库在我们获得编译器供应商对新标准的支持之前可以使用。 just::thread 是 boost::thread 库的基础。

【讨论】:

【参考方案2】:

栅栏将它们栅栏的操作(加载和存储)序列化,也就是说,在栅栏执行之前不能开始其他操作,但栅栏要等到所有前面的操作都完成后才会执行。引用 intel 使这个含义更加精确(取自 MFENCE 指令,第 3-628 页,第 2A 卷,Intel 指令参考):

这种序列化操作保证每次加载和存储 按程序顺序位于 MFENCE 指令之前的指令 在任何加载或存储指令之前变得全局可见 遵循 MFENCE 指令。1

    加载指令在以下情况下被视为全局可见 确定要加载到其目标寄存器中的值。

在 C++ 中使用栅栏很棘手(C++11 可能在某处有栅栏语义,也许其他人有这方面的信息),因为它依赖于平台和编译器。对于使用 MSVC 或 ICC 的 x86,您可以使用 _mm_lfence_mm_sfence_mm_mfence 进行加载、存储和加载 + 存储隔离(请注意,其中一些是 SSE2 指令)。

注意:这是从 Intel 的角度出发,即:使用 x86(32 或 64 位)或 IA64 处理器

【讨论】:

引用的是哪个版本的手册? (首页底部有年份和完整的6-8位数字版本) @osgx:它从 2011 年 5 月开始,订单号 325383-039US,如果您想订购副本:P 如果我的内联汇编例程是用 volatile 关键字编写的,我还需要围栏吗? @JayD:这取决于代码的作用,但volatile 只是防止从编译器端重新排序和缓存,CPU 没有任何迹象表明某些东西是/曾经是易失性的

以上是关于记忆栅栏是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

《学习之道》第三章工作记忆和长期记忆

如何让WPS文档记忆工作区窗口大小?

Trados - TM (翻译记忆库,Translation Memory)

工作记忆:大脑也需要缓存|心理词条

JavaScript的记忆函数真的可以提升性能吗?

如何快速创建Trados翻译记忆库