内存级别/栅栏 ( Memory Barriers / Fences ) – 翻译
翻译自:Martin Thompson – Memory
Barriers/Fences
在这篇文章里,我将讨论并发编程里最基础的技术–以内存关卡或栅栏著称。那让进程内的内存状态对其它进程可见。
CPU 使用了非常多技术去尝试和适应这种事实:CPU 运行单元的性能已远远超出主内存性能。在我的“Writing
Combining”文章,我仅仅是谈及当中一种技术。
CPU 使用的用来隐藏内存延迟的最普通技术是管线化指令。然后付出巨大努力和资源去尝试重排序这些管线来最小化缓存不命中的有关迟延。
当一个程序运行的时候。它不在乎。假设重排序后的指令提供了一样的终于结果。比如,在一个循环内。假设循环内没有操作使用循环计算器。循环计数器什么时候更新是不在乎的。编译器和 CPU 自由地重排序指令来最大化地利用 CPU,直到下一次迭代即将開始时才更新它(循环计数器)。
也可能,在一个循环的运行过程中。这个变量可能存储在一个寄存器里。永远不会推到缓存或主内存。因此,它对其他 CPU 永远不可见。
CPU 核包括多个运行单元。
比如。一个现代的Intel CPU 包括6个运行单元。能够做一组数学。条件逻辑和内存操作的组合。每一个运行单元能够做这些任务的组合。
这些运行单元并行地操作,同意指令并行地运行。假设从其他 CPU 来观察,这引入了程序顺序的还有一层不确定性。
终于,但缓存不命中发生时,现代 CPU 能够依据内存载入的结果做一个如果,然后基于这个如果继续运行直至实际数据的载入完毕。
提供“程序顺序”保留了 CPU 和编译器自由地做它们觉得能够提升性能的事情。
载入(load)和存储(store)到缓存和主内存是被缓冲和重排序的,使用载入(load),存储(store),和写组合(writing-combining)缓存。
这些缓存是关联的队列,同意高速查找。这样的查找是必须的,当一个稍后的载入须要读取一个之前存储的、还没有到达缓存的值时。上图描绘了现代多核 CPU 的简化视图。它显示了运行单元怎样使用本地寄存器和缓存来管理内存,与缓存子系统来回传送。
在多线程环境下,须要採用一些技术来让程序结果及时可见。
我不会在这篇文章里涉及缓存一致性。只如果一旦内存被推到缓存。然后有一个协议消息将发生。以确保全部共享数据的缓存是一致的。这样的使内存对处理器核可见的技术被称为内存关卡或栅栏。
内存关卡提供了两种属性。首先,它们保留了外部可见的程序顺序。通过确保全部的、关卡两側的指令表现出正确的程序顺序。假设从其它CPU观察。第二,它们使内存可见,通过确保数据传播到缓存子系统。
内存关卡是一个复杂的主题。它们在不同的 CPU 架构上的实现是很不同的。Intel CPU 有一个关联的强内存模型。本篇将以 x86 CPU 为基础解说。
存储关卡(store barrier)
存储关卡,在x86 上是”sfence”指令,强迫全部的、在关卡指令之前的 存储指令在关卡曾经发生,而且让 store buffers 刷新到公布这个指令的 CPU cache。这将使程序状态对其它 CPU 可见,这样,假设须要它们能够对它做出响应。一个实际的好样例是以下的、简化的、来自Disruptor的类BatchEventProcessor。当sequence被更新后。其它消费者和生产者知道这个消费者的进展。并进行适当的响应。全部在关卡之前对内存的更新如今都可见了。
private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE;
// from inside the run() method
T event = null;
long nextSequence = sequence.get() + 1L;
while (running)
{
try
{
// 译注:barrier 会读取其它sequence 的值。所以这里面有个 load barrier 指令。
final long availableSequence = barrier.waitFor(nextSequence);
while (nextSequence <= availableSequence)
{
event = ringBuffer.get(nextSequence);
boolean endOfBatch = nextSequence == availableSequence;
eventHandler.onEvent(event, nextSequence, endOfBatch);
nextSequence++;
}
sequence.set(nextSequence - 1L);
// store barrier 插入到这里 !!!
}
catch (final Exception ex)
{
exceptionHandler.handle(ex, nextSequence, event);
sequence.set(nextSequence);
// store barrier 插入到这里 !!!
nextSequence++;
}
}
载入关卡(load barrier)
载入关卡。在x86 上是”lfence”指令,强迫全部的、载入指令之后的指令在关卡之后发生,然后等待那个 CPU 的 load buffer 排空。
这使其他 CPU 暴露出来的程序状态对这个 CPU 可见。在做出很多其他进展之前。
这个的一个好样例是前面引用的 BatchEventProcessor 的 sequence 被其他生产者或消费者读取时,Disruptor 里有等价的指令。
Full Barrier
Full Barrier。在x86 上是”mfence”指令,在 CPU 上是载入和存储关卡的组合。
Java 存储模型(Java Memory Model)
在Java 存储模型里,volatile 字段在写入后插入一个存储关卡,在读取前插入载入关卡。类里面修饰为 final 的字段在它们被初始化后插入一个存储指令,以确这些字段在构造函数完毕、有可用引用到这个对象时是可见的。
原子指令和软件锁(Atomic Instructions and Software Locks)
原子指令,如x86里的 “lock …” 指令,是高效的 full barrier,它们锁住存储子系统来运行操作。有受保证的全序关系(total order),即使跨 CPU。软件锁通常使用存储关卡,或原子指令来达到可视性和保留程序顺序。
存储关卡对性能的影响(Performance Impact of Memory Barriers)
存储关卡阻止了 CPU 运行非常多隐藏内存延迟的技术,因此有它们有显著的性能开销,必须考虑。为了达到最大性能。最好对问题建模,这样处理器能够做工作单元,然后让全部必须的存储关卡在工作单元的边界上发生。採用这样的方法同意处理器不受限制地优化工作单元。
把必须的存储关卡分组是故意的,那样,在第一个之后的 buffer 刷新的开销会小点。由于没有工作须要进行又一次填充它。
译注:关于Disruptor能够看我之前的这篇文章:http://coderbee.net/index.php/open-source/20130812/400
原文地址:http://coderbee.net/index.php/concurrent/20131211/624;
内存级别/栅栏 ( Memory Barriers / Fences ) – 翻译的更多相关文章
- 什么是内存屏障? Why Memory Barriers ?
要了解如何使用memory barrier,最好的方法是明白它为什么存在.CPU硬件设计为了提高指令的执行速度,增设了两个缓冲区(store buffer, invalidate que ...
- 洛谷P2731 骑马修栅栏 Riding the Fences
P2731 骑马修栅栏 Riding the Fences• o 119通过o 468提交• 题目提供者该用户不存在• 标签USACO• 难度普及+/提高 提交 讨论 题解 最新讨论 • 数据有问题题 ...
- Android 内存管理 &Memory Leak & OOM 分析
1.Android 流程管理&内存 Android主要应用在嵌入式设备其中.而嵌入式设备因为一些众所周知的条件限制,通常都不会有非常高的配置,特别是内存是比較有限的. 假设我们编写的代 码其中 ...
- Memory Barriers
这回该进入主题了. 上一文最后提到了 Memory Barriers ,即内存屏障.由于对一个 CPU 而言,a = 1; b = 1. 由于在中间加了内存屏障,在 X86 架构下,就 ...
- Synthesis of memory barriers
A framework is provided for automatic inference of memory fences in concurrent programs. A method is ...
- Java内存模型(Java Memory Model,JMM)
今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...
- 内存转储文件 Memory.dmp
https://baike.sogou.com/v63435711.htm?fromTitle=内存转存文件 内存转储是用于系统崩溃时,将内存中的数据转储保存在转储文件中,供给有关人员进行排错分析用途 ...
- 【并行计算-CUDA开发】关于共享内存(shared memory)和存储体(bank)的事实和疑惑
关于共享内存(shared memory)和存储体(bank)的事实和疑惑 主要是在研究访问共享内存会产生bank conflict时,自己产生的疑惑.对于这点疑惑,网上都没有相关描述, 不管是国内还 ...
- 内存管理(memory allocation内存分配)
Memory management is the act of managing computer memory. The essential requirement of memory manage ...
随机推荐
- HDU - 5036 Explosion
Problem Description Everyone knows Matt enjoys playing games very much. Now, he is playing such a ga ...
- Swift - 访问通讯录联系人(使用系统提供的通讯录交互界面)
1,通讯录访问介绍 通讯录(或叫地址簿,电话簿)是一个数据库,里面储存了联系人的相关信息.要实现访问通讯录有如下两种方式: (1)AddressBook.framework框架 : 没有界面,通过代码 ...
- Android异步载入全解析之使用多线程
异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutC ...
- 同一个form里,不管哪个 submit 都是直接提交form表单里的内容
要达到你的目的,就不能用类型为 submit 的按钮,要用 button,然后加onclick 方法来自定义预处理参数,然后再调用 submit 方法提交表单,比如 <script type=& ...
- Spring Boot 分布式Session状态保存Redis
在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而打到另外一台服务器的时候,session丢失. 常规的解决方 ...
- dos批量替换当前目录后缀名
有时候有些后缀名不满足条件,就需要进行批量的替换,如果人为的去替换,那么如果量少的话还好说,量多的话一个个去替换就太傻了,今天从网络上面查找了一些批量替换的dos命令,用起来还挺好用的,就直接把代码贴 ...
- [每日一题] 11gOCP 1z0-052 :2013-08-30 差异的增量备份.....................................................A1
转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/10669381 正确答案:AD 执行增量备份操作时,首先需要的是增量基本备份(increme ...
- 开发人员福利!ChromeSnifferPlus 插件正式登陆 Chrome Web Store
今天(2014-10-30)下午,ChromeSnifferPlus 插件正式登陆 Chrome Web Store. 在线安装地址: https://chrome.google.com/websto ...
- javascript(七)document.write
<h1>test</h1> <button type="button" onclick="my_function">点击me ...
- CImageList使用指南
在MFC中CImageList类封装了图像列表控件的功能,图像列表是一个具有相同大小的图像(可以是不同类型)的集合,其主要用于应用程序中大规模图标的存储.该控件是不可见的,通常与其它如CListBox ...