内存栅栏:软件高手的硬件观(二)

浅谈volatile关键字与内存栅栏(barrier) 内存屏障 <1>volatile volatile的本意是“易变的” 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化,但有可能会读脏数据。当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据, 即使它前面的指令刚刚从该处读取过数据。 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄... 阅读详情

因翻译水平有限,如有不妥,敬请指正和谅解!
原文下载地址:
http://download.youkuaiyun.com/download/programresearch/9829674

3.存储导致不必要的停顿

  虽然显示在图1中的缓存结构,对重复的从指定的CPU读和写指定的数据项提供了好的性能,但它的性能对于第一次写一个指定的缓存行却相当糟糕.为此考虑图4,它显示了由CPU0写一个由CPU1缓存持有的缓存行.由于CPU0必须等待缓存行到达,在它可写前,CPU0必须停顿一个额外的时间.
  写有不必要的延时
  但是,没有实际原因去强制CPU0去停顿如此长的时间—-毕竟,无论CPU1发送到缓存行的是什么数据,CPU0都继续无条件的覆盖它.

3.1 存储缓冲器

  一种方式去阻止此不必要的写停顿,是去添加”存储缓冲器”在各CPU和它的缓存间,如图5中所示,随着添加这些存储缓冲器,CPU0可以在它存储缓冲中,简单记录它的写数据,并继续执行.当缓存行最终从CPU1移动到CPU0时,数据将从存储缓冲区被移动到缓存行.
  有存储缓冲的缓存
  然而,有一些难题必须被解决,这些在后面两节中讲到.

3.2 存储转发

来看第一个难题,违反自洽性,考虑如下代码,有变量”a”和”b”都初始化为0,并且包含变量”a”的缓存行初始由CPU1所拥有,包含变量”b”的初始由CPU0拥有:

1  a=1;
2  b=a+1;
3  assert(b==2);

没人希望断言失败.然而,如果有人非常愚蠢的使用图5中极其简陋的结构,他将非常吃惊,这样一个系统可能潜在地见到如下序列的事件:

  1. CPU0启动执行a=1;
  2. CPU0查看”a”在缓存中,并知道它未命中;
  3. CPU0因而发送一个”读无效”消息,为了获得包含”a”的缓存行的独占;
  4. CPU0记录存储”a”到它的存储缓冲中;
  5. CPU1接收”读无效”消息,并通过发送缓存行来响应,并从它的缓存中移除缓存行;
  6. CPU0启动执行b=a+1;
  7. CPU0从CPU1接收缓存行,这仍然是有一个0值的”a”;
  8. CPU0从它的缓存中装载”a”,并找到值0;
  9. CPU0应用它存储缓存队列中的项到新到达的缓存行,在它的缓存中设置”a”的值为1;
  10. CPU0加1到上面装载的”a”值0,并存储它到包含”b”的缓存行(这假设已经被CPU0所拥有);
  11. CPU0执行assert(b==2),这将失败;

 问题在于有两个”a”的副本,一个在缓存中,另一个在存储缓冲区中.
 此例违反了一个非常重要的保证,称为各CPU将总看到它自己的操作,就像它们是按程序顺序发生的.
 这种保证对软件类型来说是极为直观的,因此硬件同事怜惜,并实现了”存储转发”,当执行装载时,每个CPU引用(或”检测”)它自己的存储缓冲区(store buffer),如同它的缓存一样,如图6中所示.也就是说,一个给定”CPU”的存储是直接转发给它自己后续装载,而不必传递到缓存.
有存储转发的缓存
由于在相应位置有存储转发,上面序列的第8项,将在存储缓冲区找到正确的”a”值,因此”b”的最终值将是2,如所期待的.

3.3 存储缓冲区和内存栅栏
来看第二个难题,违反全局内存排序,考虑如下代码序列,变量”a”和”b”初始化为0:

1  void foo(void)
2  {
3    a = 1;
4    b = 1;
5  }
6
7  void bar(void)
8  {
9    while(b==0) continue;
10   assert(a == 1);
11 }

假设CPU0执行foo()和CPU1执行bar(),进一步假设包含”a”的缓存行仅驻留在CPU1的缓存中,并且包含”b”的缓存行由CPU0所拥有,则操作序列可能如下:

  1. CPU0执行a=1,缓存行不在CPU0的缓存中,因此CPU0放置”a”的新值在它的存储缓冲区(store buffer),并且转发一个”读无效”消息.
  2. CPU1执行while(b==0)continue;但包含”b”的缓存行不在它的缓存中.它因此发送一个”读”消息.
  3. CPU0执行b=1,它已经拥有此缓存行(换句话说,缓存行已经在”修改”或”独占”状态),因此它存储”b”的新值在它的缓存行中.
  4. CPU0接收”读”消息,并发送包含最新”b”值的缓存行到CPU1,也标记为”共享”在它自己的缓存中.
  5. CPU1接收包含”b”的缓存行,并安装它在它的缓存.
  6. CPU1现在能完成执行while(b==0)continue;并因此知道”b”的值是1,它继续处理下一条语句.
  7. CPU1执行assert(a==1),并因此CPU1工作用”a”的旧值,此断言失败.
  8. CPU1接收”读无效”消息,并且发送包含”a”的缓存行到CPU0,并无效此缓存行从它所拥有的缓存,但是太迟了.
  9. CPU0接收包含”a”的缓存行,并立即应用缓冲的存储到断言失败的受害者CPU1.

硬件设计者不能在此直接帮助,由于CPU不知道哪个变量是相关的,更不用说它们是如何相关的.因此,硬件设计者提供内存栅栏指令去允许软件告诉CPU关于这些关系.程序片段必须被更新为包含内存栅栏:

1  void foo(void)
2  {
3    a = 1;
4    smp_mb();
5    b = 1;
6  }
7
8  void bar(void)
9  {
10   while(b == 0)continue;
11   assert(a == 1);
12 }

内存栅栏smp_mb()将引发CPU去刷新它的存储缓冲区,在应用后续存储到它们的缓存行前.在继续处理前,CPU将简单的停顿,直到存储缓冲区为空,或它可以使用存储缓冲区去持有后续存储,直到所有存储在缓冲区的先前项被应用.
因此,随后的操作序列可能如下:

  1. CPU0执行a=1. 缓存行不在CPU0的缓存中,因此CPU0放置”a”的新值在它的存储缓冲区(store buffer)中,并发送一个”读无效”消息.

  2. CPU1执行while(b==0)continue;但包含”b”的缓存行不在它的缓存中,它因此发送一个”读”消息.

  3. CPU0执行smp_mp(),并且标记所有当前存储缓冲区项(即,a=1);

  4. CPU0执行b=1,它已经拥有此缓存行(也就是说,缓存行已经在”修改”或”独占”状态),但有一个标记项在存储缓冲区(store buffer)中.因而,不是存储一个新的”b”值到缓存行,而是替代放置它d到存储缓冲区(store buffer)中(但在一个未标记的项/条目中).

  5. CPU0接收”读”消息,并发送包含”b”原始值的缓存行到CPU1.它也标记它所拥有的缓存行副本为”共享的”.

  6. CPU1接收包含”b”的缓存行,并装载它到缓存.

  7. CPU1现在能完成执行while(b==0)continue;但由于它知道”b”值仍为0,它重复while语句.”b”的新值被安全隐藏在CPU0的存储缓冲区.

  8. CPU1接收”读无效”消息,并发送包含”a”的缓存行到CPU0,并从它自己的缓存中无效此缓存行.

  9. CPU0接收包含”a”的缓存行,并应用缓冲的存储.

  10. 由于a的存储,是在存储缓冲区中仅有的被smp_mb()标记的项,CPU0也能存储”b”的新值–除了实际上包含”b”的缓存行现在是”共享”状态.

11.CPU0因此发送一个”无效”消息到CPU1

12.CPU1接收”无效”消息,从缓存中无效包含”b”的缓存行,并发送一个”确认”消息到CPU0.

13.CPU1执行while(b==0)continue,但包含”b”的缓存行不在它的缓存中.它因而发送一个”读”消息到CPU0.

14.CPU0接收”确认”消息,并设置包含”b”的缓存行为”独占”状态.CPU0现在存储”b”的新值到缓存行.

15.CPU0接收”读”消息,并发送包含原始”b”值的缓存行到CPU1,它也标记它的缓存行副本为”共享”.

16.CPU1接收包含”b”的缓存行,并装载它到缓存中.

17.CPU1现在能结束执行while(b==0)continue;并且由于它知道”b”的值为1,它继续下一条语句.

18.CPU1执行assert(a==1),但包含”a”的缓存行不在它的缓存中.一旦它从CPU0获得此缓存,它将用”a”的最新值工作,并且断言因此通过.

如你所见,此过程调用了不少的记录.即使一些直观上简单的,如”装载a的值”,在处理器中也会调用大量复杂的步骤.

CUDA内存栅栏与线程同步:告别玄学崩溃的完整指南 GPU并行编程中,线程间的内存可见性往往是比语法更隐蔽的陷阱。由于CUDA采用弱内存模型,一个线程的写入何时对其他线程可见,并不由代码顺序天然保证。内存栅栏(fence)与同步函数(如__syncthreads)正是解决这一问题的核心原语:前者约束可见顺序,后者约束执行顺序。理解两者区别与配合,是构建可靠跨block通信、生产者-消费者模式的基础。本文从实际kernel开发中常见的偶发错误切入,梳理CUDA内存栅栏的API、volatile的局限以及cuda::atomic的现代用法,帮助开发者从根源上排查 阅读详情

相关推荐

Linux内存栅栏概述

Linux内存栅栏概述 Linux内存栅栏概述 Linux内存栅栏概述

内存屏障(memory fence)是什么? 它有什么用?

通过插入适当的内存屏障指令,可以控制不同线程之间的操作顺序,避免出现数据竞争和并发错误,从而提供正确的多线程同步和通信。全能屏障是一种全局的同步操作,它确保在屏障之前的所有内存操作都完成后,才能执行屏障之后的内存操作。内存屏障分为两种类型:全能屏障(full barrier)和部分屏障(acquire/release barrier)。获取屏障用于保证在屏障之前的所有读操作完成后,才能执行屏障之后的读操作。释放屏障用于保证在屏障之前的所有写操作完成后,才能执行屏障之后的写操作。

haozizang的博客 1682

方差分解分析变量筛选与显著性计算

冗余分析(redundancy analysis,RDA)是一种回归分析结合主成分分析的排序方法,也是多响应变量(multiresponse)回归分析的拓展。在进行VPA时,首先就要对这些环境因子进行一个分类,然后在约束其它类环境因子的情况下,对某一类环境因子进行排序分析,这种分析也成为偏分析,即partialRDA。在地球环境科学的实际应用中,可以基于VPA结果得出不同类型的环境因素(如何气候、土壤性质以及植物)对生物群落组成(如植被丰度,微生物群落)的解释程度。对于由或创建的排序对象,函数。

wlh2067的博客 2521

C++编程:内存栅栏(Memory Barrier)详解及在多线程编程中的应用

在多线程编程中,内存操作的顺序一致性是一个至关重要的问题。为了确保共享数据在不同线程间的正确性,我们通常会用到**内存栅栏(Memory Barrier)**。本文将详细讲解内存栅栏的概念、为什么需要它、其背后的本质原因,以及在不同架构(如ARM64和x86)下的表现差异。

Where there is life, there is hope. 2671

MDM主数据清洗和编码集成说明

绝大多数的企业业务系统是伴随业务发展而建设的,但随着业务发展单一系统难以支撑企业业务闭环。MDM主数据管理平台可以满足企业数据治理的需求,本文针对主数据清洗和编码集成进行梳理。...

数通畅联 3321

保姆级教程:用Python仿真OFDM系统(附抗多径干扰优化方案)

本文提供了一份详细的Python仿真OFDM通信系统教程。通过NumPy和Matplotlib,从二进制比特流开始,逐步构建完整的OFDM系统模型,涵盖IFFT、循环前缀等核心概念。重点探讨了如何对抗多径干扰,并提供了优化方案,通过误码率曲线直观对比性能,适合通信工程学习和物理层开发实践。

weixin_29230649的博客 232

内存屏障是什么?

内存屏障、内存栅栏是什么?内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。---百度百科个人理解:就类似于我们喝茶的时候需要先把水煮开(限定条件),然后再切茶,而这一整套流程都是限定特定环节的先后顺序(内存屏障),保障切出来的茶可以更香。为什么会有内存屏障?内存...

逍遥壮士 1071

Unity新手必看:5分钟搞定TextMeshPro中文乱码,告别小方块(附7000+常用字库)

本文详细介绍了Unity中TextMeshPro中文乱码问题的解决方案,包括快速生成中文字体的步骤和优化技巧。通过使用支持中文的.ttf字体文件和自定义字符集,开发者可以在5分钟内解决TMP显示小方块的问题,提升游戏或应用的文本显示效果。

weixin_33095355的博客 363

并发编程(14)——内存栅栏

栅栏主要用于强制施加内存次序,却无须更改任何数据,通常于服从次序的原子操作组合使用。用大白话来说,栅栏用于阻止编译器或CPU对某些内存操作进行重排,确保在它之前的操作完成后,才会执行它之后的操作,从而维护内存操作的顺序一致性。因为是最宽松的内存序,它只能保证原子性,却不能保证多线程之间的先行性和顺序性,所以服从次序的原子操作通常会通过编译器进行指令重排,而栅栏会限制这种重排。我们之前学习了互斥量mutex:拿到mutex锁的线程将拥有唯一进入临界区的资格(共享互斥除外)。其实mutex。

m0_63086198的博客 1472

在VSCode中使用LaTex+(grammarly语法检测,chatgpt翻译,Sumatra PDF 轻量级pdf阅读器)

整个文章分为以下几个内容,打 * 的是必须要安装的。

u011895157的博客 2万+

VIIRS-NPP夜间灯光遥感数据下载和预处理

右侧搜索框搜索“按掩膜提取”,提取出夜光影像中国区域。将其转换为Albers积投影和重采样到400m。点击右上角Export,选择shp格式。最后点export导出就行。点击复制按钮,来到这个。,ctrl + v粘贴。将小于0.3的值置为0。下载好月合成产品后,

热爱分享,热爱记录 1万+

内存次序之内存栅栏

内存次序管理工具,内存栅栏的使用和介绍。

weixin_45556596的博客 1132

LSTM 深度解析从原理推演到现代序列建模的演进

LSTM(Long Short-Term Memory,长短期记忆网络)是一种特殊的递归神经网络(RNN),它通过引入记忆单元(Cell State)来解决普通RNN在长序列数据处理中的梯度消失问题。LSTM可以在长时间的序列中捕捉依赖关系,是一种非常适合处理时间序列、自然语言处理、语音识别等任务的深度学习模型。在LSTM中,记忆单元(Cell State)是一个贯穿整个序列的数据通道,用于存储和传递关键信息。记忆单元在LSTM的时间步之间保持信息的持久性,可以看作是一个长期记忆的“容器”。

hello.reader 3万+

内存栅栏:软件高手的硬件观(四)

因翻译水平有限,如有不妥,敬请指正和谅解! 原文下载地址: http://download.youkuaiyun.com/download/programresearch/98296746. 样例 内存栅栏序列此段介绍一些巧妙,但隐约有问题的内存栅栏的使用.尽管它们中很多在大多数时候可以工作,并且一些在某些特定的CPU上可以一直工作,这种用法必须避免,如果目的是为了产生在所有CPU上工作可靠的代码.为了辅助

从头来过 1009

3、Tone Mapping色调映射(Advanced High Dynamic Range Imaging )

3、Tone Mapping色调映射(Advanced High Dynamic Range Imaging) 如今,大多数可用的显示设备无法本地显示HDR内容。 入门级显示器的对比度仅为200:1。 尽管高端液晶电视具有更高的对比度,但平均对比度约为10,000:1,但通常每个色彩通道的离散度为8位,很少为10位。 这意味着色调限制为255。在过去的二十年中,研究人员花费了大量时间和精力来压缩HDR图像和视频的范围,以便可以在LDR显示器上更“自然”地看到数据。 图3.1 色调映射和真实场景之间.

ISP行业的忠实粉丝 1866

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

内存栅栏:软件高手的硬件观(三)

4. 存储序列导致不必要的停顿  不幸的是,每个存储缓冲区必须相对小,这意味着CPU执行一个适当的序列存储能填满它的储存缓冲区(例如,如果它们所有的都导致缓存未命中).在那时,CPU必须再一次等待无效去完成,为了排空它的存储缓冲,在它能继续执行前.此类情况可立即出现在内存栅栏之后,而所有后续存储指令必须等待使无效操作去完成,无论是否这些存储导致缓存未命中.  此状态可被改进通过使标记无效确认消息尽

从头来过 621

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

内存栅栏:软件高手的硬件观(一)

因翻译水平有限,如有不妥,敬请指正和谅解! 原文下载地址: http://download.youkuaiyun.com/download/programresearch/9829674是什么让疯狂的CPU设计者们强加内存栅栏给可怜而毫不知情的SMP(对称多处理器系统)软件设计者? 简而言之,因为内存引用的重排序可以达到更好的性能.因此在像同步原语这样的事上,需要用内存栅栏来强制排序.同步原语操作依

从头来过 952

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

计算机原理—内存栅栏深入分析

本文系统介绍了内存栅栏的概念、背景、底层机制及应用。内存栅栏作为一种低级同步原语,通过建立内存序约束来保证多线程环境下数据操作的顺序性和可见性。文章分析了编译器优化、CPU流水线及多核架构导致的内存乱序问题,指出内存栅栏通过硬件指令和编译器约束解决这些问题。不同平台(如X86和ARM)的内存模型差异导致栅栏实现机制不同。文章还列举了常见使用误区,如栅栏位置错误和配对不当,并通过双检查锁案例展示了内存栅栏的实际应用。最后强调底层技术理解对开发者正确使用同步机制的重要性。

fpcc的专栏 846

跟我学C++中级篇—内存栅栏和内存序

本文分析了内存栅栏和内存序的区别与联系。内存栅栏是底层指令级机制,影响线程所有内存操作,而内存序是高级抽象,仅针对特定原子操作。二者目的相同,但内存序更灵活、可移植性更好,推荐优先使用。文章通过示例展示了acquire-release栅栏的应用场景,强调它们必须成对使用才能保证同步效果。最后指出,理解软硬件各层次的抽象模型对技术本质的把握至关重要。

fpcc的专栏 573

C++原子操作背后的秘密:内存栅栏如何掌控CPU指令重排?

掌握C++多线程同步难题?深入解析内存栅栏如何抑制CPU指令重排,结合原子操作实现高效同步。涵盖常见竞争场景、memory_order选择策略及性能优化技巧。理解C++栅栏同步技巧的关键机制,提升并发编程可靠性,值得收藏。

DeepLens的博客 336

揭秘C++内存栅栏:如何正确使用std::atomic_thread_fence避免数据竞争?

掌握C++栅栏同步技巧,有效避免多线程数据竞争。详解std::atomic_thread_fence在无锁编程中的应用,揭示内存顺序控制的核心机制与典型场景。提升并发性能与安全性,值得收藏。

ByteShoal的博客 543

跟我学C++中级篇——内存屏障内存栅栏和编译器屏障以及相关

有锁和无锁就如武学上的有剑和无剑,重要的不是剑,是一种对内存原理的根本性的理解。不要对一些技术奉为圭臬,因为每一种技术一定有它的长处和短处。也就是常说的应用场景,只有会灵活运用,才是自由的编程。

fpcc的专栏 1672

内存栅栏和内存对齐

 Cache 一致性问题单核 Cache 中每个 Cache line 有2个标志:dirty 和 valid 标志,它们很好的描述了 Cache 和 Memory 之间的数据关系(数据是否有效,数据是否被修改),而在多核处理器中,多个核会共享一些数据。  只有 Core 0 访问变量 x,它的 Cache line 数据和内存中的数据一致,数据只存在于本 Cache 中。  3个 Core...

lgshendy的专栏 204

线程同步难题一网打尽,深度解析CUDA中的__syncthreads()与内存栅栏

掌握C语言CUDA的线程同步关键技巧,深入解析__syncthreads()与内存栅栏的协同机制。适用于共享内存数据一致性保障、线程块内同步等场景,提升并行效率。详解适用条件与性能优化策略,避免死锁与数据竞争。线程同步难题一网打尽,值得收藏。

PixelIsle的博客 1052

CUDA内存栅栏与同步函数:跨线程通信的可见性实战指南

在GPU并行计算中,内存可见性与线程同步是决定程序正确性的基石。CUDA C++通过内存栅栏函数与同步函数控制跨线程的内存访问顺序,确保数据在多线程间可靠传递。内存栅栏(如__threadfence)解决硬件层可见性顺序,同步函数(如__syncthreads)则提供块级执行会合。在跨block通信、共享内存复用等场景,若缺乏正确的同步机制,常导致随机错误。本文深入解析CUDA内存栅栏与同步函数的语义差异、与原子操作及volatile的配合方式,并结合实际工程总结故障排查套路,为并行程序开发提供实操参考。

weixin_29172201的博客 257

【C++ 内存序】内存栅栏的真相:彻底理解 C++ atomic_thread_fence

并发编程是一门同时考验思维严谨度与创造力的技术艺术。在 C++11 之前,我们对于多线程内存可见性和指令重排往往缺乏明确的标准保障;C++11 引入了一套完整的内存模型,使得“原子操作”与“内存序”成为并发领域中不可或缺的核心概念,其中 `std::atomic_thread_fence` 更像是一道“全局道路护栏”,为开发者在多线程环境下提供了更细腻的顺序控制。本章将围绕 `std::atomic_thread_fence` 的基本概念、背景动机以及它在并发编程中的意义展开,帮助读者在后续更深入的学习中奠

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 1995
上一篇: 内存栅栏:软件高手的硬件观(一)
下一篇: 内存栅栏:软件高手的硬件观(三)
ProgramResearch
博客等级 码龄11年 0粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值