c++ - 内存屏障/栅栏的开销

标签 c++

我目前正在编写 C++ 代码并在我的代码中使用了很多内存屏障/栅栏。我知道,MB 告诉编译器和硬件不要重新排序围绕它的写入/读取。但我不知道这个操作在运行时对处理器来说有多复杂。

我的问题是:这种屏障的运行时开销是多少?我没有用谷歌找到任何有用的答案...... 开销可以忽略不计吗?或者导致大量使用 MB 导致严重的性能问题?

最好的问候。

最佳答案

与算术和“正常”指令相比,我知道这些指令非常昂贵,但没有数字来支持该声明。我喜欢 jalf 的回答,描述了指令的效果,并想补充一点。

通常存在几种不同类型的障碍,因此了解它们之间的差异可能会有所帮助。在清除锁定字(例如 ppc 上的 lwsync,或 ia64 上的 st4.rel)之前,例如在互斥实现中需要像 jalf 提到的屏障。所有读取和写入都必须完成,并且只能执行管道中没有内存访问且不依赖于正在进行的内存操作的后续指令。

另一种类型的屏障是您在获取锁时在互斥实现中使用的类型(例如,ppc 上的 isync 或 ia64 上的 instr.acq)。这对以后的指令有影响,所以如果一个非依赖加载已经被预取,它必须被丢弃。示例:

if ( pSharedMem->atomic.bit_is_set() ) // use a bit to flag that somethingElse is "ready"
{
   foo( pSharedMem->somethingElse ) ;
}

如果没有获取屏障(借用 ia64 术语),如果 somethingElse 在标记位检查完成之前将其写入寄存器,您的程序可能会产生意外结果。

还有第三种类型的屏障,通常较少使用,并且需要强制执行商店加载顺序。此类强制执行指令的指令示例包括:ppc 上的同步(重量级同步)、ia64 上的 MF、sparc 上的 membar #storeload(甚至对于 TSO 也是必需的)。

用ia64之类的伪代码来说明,假设有

st4.rel
ld4.acq

如果中间没有 mf,则无法保证负载跟随存储。您知道 st4.rel 之前的加载和存储是在该存储或“后续”加载之前完成的,但是该加载或其他 future 加载(如果不依赖则可能存储?)可能会潜入,提前完成,因为没有什么可以阻止否则。

因为互斥实现很可能只在它们的实现中使用获取和释放屏障,我预计这样做的一个可观察到的效果是锁释放后的内存访问实际上有时可能会在“仍在关键部分”时发生。

关于c++ - 内存屏障/栅栏的开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1815282/

相关文章:

c++ - 在运行时禁用 gcov 覆盖

c++ - 并行多次调用一个函数

c++ - "' SpaceShip ' does not name a type"即使 SpaceShip 肯定是一种类型

c++ - 我如何故意触发 fgets() 中的错误?

c++ - 在 SPOJ INCSEQ 中获取 WA - 增加子序列

C++ curl : treating header and body data differently

c++ - 处理多个颠覆项目之间的关系

c++ - 用于工作线程池和作业队列等的可移植库

c++ - SFML 2.0:Keyboard::isKeyPressed 并不总是正确返回

c++ - g++ 重载运算符优化