C++ 记忆障碍是如何工作的?
在Windows下,有三个编译器内部函数来实现内存屏障:C++ 记忆障碍是如何工作的?,c++,c,assembly,execution,instructions,C++,C,Assembly,Execution,Instructions,在Windows下,有三个编译器内部函数来实现内存屏障: 1. _ReadBarrier; 2. _WriteBarrier; 3. _ReadWriteBarrier; 然而,我发现了一个奇怪的问题:_ReadBarrier似乎是一个什么都不做的伪函数!下面是我用VC++2012生成的汇编代码 我的问题是:如何在汇编指令中实现内存屏障功能 int main() { 013EEE10 push ebp 013EEE11 mov ebp,esp
1. _ReadBarrier;
2. _WriteBarrier;
3. _ReadWriteBarrier;
然而,我发现了一个奇怪的问题:_ReadBarrier似乎是一个什么都不做的伪函数!下面是我用VC++2012生成的汇编代码
我的问题是:如何在汇编指令中实现内存屏障功能
int main()
{
013EEE10 push ebp
013EEE11 mov ebp,esp
013EEE13 sub esp,0CCh
013EEE19 push ebx
013EEE1A push esi
013EEE1B push edi
013EEE1C lea edi,[ebp-0CCh]
013EEE22 mov ecx,33h
013EEE27 mov eax,0CCCCCCCCh
013EEE2C rep stos dword ptr es:[edi]
int n = 0;
013EEE2E mov dword ptr [n],0
n = n + 1;
013EEE35 mov eax,dword ptr [n]
013EEE38 add eax,1
013EEE3B mov dword ptr [n],eax
_ReadBarrier();
n = n + 1;
013EEE3E mov eax,dword ptr [n]
013EEE41 add eax,1
013EEE44 mov dword ptr [n],eax
}
013EEE56 xor eax,eax
013EEE58 pop edi
013EEE59 pop esi
013EEE5A pop ebx
013EEE5B add esp,0CCh
013EEE61 cmp ebp,esp
013EEE63 call __RTC_CheckEsp (013EC3B0h)
013EEE68 mov esp,ebp
013EEE6A pop ebp
013EEE6B ret
有几个重要的问题要考虑。也许是 首先,屏障只在多线程中起作用 大多数编译器需要一个特殊的选项来生成 多线程代码。而像
\u ReadBarrier
这样的东西几乎是
当然,编译器内置,除非
您已经给出了多线程代码的选项
第二个是硬件所需要的,即使是在
多线程上下文不同。在我见过的大多数机器上
在这台机器上工作了(四十多年),从来都不需要
任何东西仅当机器已关闭时,屏障才相关
复杂的读写管道。(大多数早期机器
甚至没有围栏或屏障说明,因此生成
代码必须是空的。)
\u ReadBarrier
,\u WriteBarrier
和\u ReadWriteBarrier
是;它们与CPU内存屏障完全无关,只对特定类型的内存有效(请参阅“受影响的内存”)
是用于强制设置CPU内存屏障的固有属性。然而,微软的建议是在VC++中使用
std::atomic
。现代处理器能够执行的指令远远超过它实际“完成”指令的位置,因此,当涉及到某些类型的内存操作时,内存屏障被用来防止它向前运行,在这些操作中,需要严格的排序——对于大多数情况,实际上,在变量b之前写入变量a,或者在变量a之前写入变量b并不重要。但有时确实如此
x86指令集具有lfence
、sfence
和fence
,它们是“fence in”分别加载、存储和所有内存操作的指令。关于“栅栏”或“屏障”指令的要点是确保屏障指令之前的所有指令在屏障后的下一条指令可以继续之前已完成加载、存储或两者
如果您正在实现例如信号量、互斥量或类似的指令,这一点很重要,因为在继续读取其他数据之前,存储表示“我已锁定信号量”的值很重要。否则事情可能会出问题,比如说
请注意,除非您真的知道如何使用内存屏障,否则最好不要使用它们——并且依赖于已经存在的解决相同问题的代码——std::atomic
是资助此类代码的一个地方。我已经写了很多“棘手”的代码,但只有一两次我需要在代码中设置内存障碍
有好几次,我需要使编译器不散布代码,这可以通过“无操作函数”来实现,显然现在甚至有特殊的内在函数来实现 在x86上,所有加载都是获取,所有存储都是发布,因此不需要任何显式代码。唯一必要的代码是完整的屏障。读屏障的概念比x86体系结构更为普遍,在内存模型较弱的机器上也很重要。我猜这些只是改变编译器生成代码方式的编译器屏障,而不是汇编级屏障。@KerrekSB即使是最新的机器,这真的是真的吗?这样做会大大降低速度,并消除使用多核的许多效用(至少对于某些类型的应用程序是如此)。@JamesKanze:Hm,至少就我所知。不过,我完全有可能弄错了。linux内核smp内存障碍的一个很好的解释也与低级硬件访问相关。@edgar.holleis是真的。当然,这不会影响用户进程,但真正的标准不是线程,而是两个实体访问内存的可能性。线程只是发生这种情况的最常见的情况。