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

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

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

4. 存储序列导致不必要的停顿

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

4.1 无效队列

无效确认消息花费如此之长(时间)的原因之一,是因为必须确保对应的缓存行实际是无效的,并且此无效会被延迟,如果缓存忙,例如,如果CPU是集中的装载和存储所有这些被放置在缓存中的数据.另外,如果大量的无效消息在短时间内到达,给定的CPU可能在进度上落后处理它们,因此可能停顿所有其他的CPU.

然而,CPU不需要实际无效缓存行,在发送确认前.它将替代使用入队列无效消息,当知道,消息将被在CPU发送关于此缓存行任何进一步的消息前处理.

4.2 无效队列和无效确认

图7显示一个有无效队列的系统.一个有无效队列的CPU可在无效消息被放置入队列中时确认,替代必须等待直到对应的缓存行实际无效.当然,CPU必须引用它的无效队列,当准备发送无效消息时—-如果对应缓存行的一项在无效队列中,CPU不能立即发送无效消息;它必须替换等待直到无效队列项被处理完成.
有无效队列的缓存.png

放置一项到无效队列本质上是CPU允许在发送关于此缓存行的任何MESI协议消息前处理此项.只要对应的数据结构没有高并发,CPU将很少因此许可被打扰.

然而,事实上无效消息被缓存到无效队列中,提供了额外的内存重排的机会,如下一节讨论.

4.3 无效队列和内存栅栏

假设”a”和”b”的值初始化为0,即”a”是只读的副本(MESI”共享”状态),并且”b”由CPU0所有(MESI”独占”或”修改”状态).接着假设,CPU0执行foo(),而CPU1执行函数bar(),按下面代码片段:

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 }

接着操作序列可能如下:

1.CPU0执行a=1.对应的缓存行在CPU0缓存中是只读的,因此CPU0放置”a”的新值在它的存储缓冲中,并且发送一个无效消息为了刷新来自于CPU1的缓存的对应的缓存行.

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

3.CPU0执行b=1,它已经拥有此缓存行(换句话说,缓存行已经在”修改”或”独占”状态下),因此它存储”b”的新值到它的缓存行中.

4.CPU0接收”读”消息,并发送包含最新更新的”b”的缓存行到CPU1,也在它自己的缓存中标记行为”共享”.

5.CPU1接收”a”的无效消息,放置它到它的无效队列,并发送一个”无效确认”消息到CPU0.注意:”a”的旧值仍然存在于CPU1的缓存中.

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

7.CPU1现在可以结束执行while(b==0)continue,并由于它找到”b”的值为1,它继续下一条语句.

8.CPU1执行assert(a==1),并且由于”a”的旧值仍在CPU1的缓存中,此断言失败.

9.CPU1处理队列的”无效”消息,并从它自己的缓存中无效包含”a”的缓存行.但这太迟了.

10.CPU0接收到来至CPU0(译者注:应为CPU1)对于”a”的”无效确认”消息,并立即应用缓冲的存储到断言失败的受害者CPU1.

对此情况,CPU设计者再一次地无计可施,由于硬件不可能知道CPU放置的不同bits之间的关系.然而,内存栅栏指令能交互影响无效队列,因此当一个给定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   smp_mb();
12   assert(a == 1);
13 }

有了这些改变,操作序列可能如下:

1.CPU0执行a=1.在CPU0的缓存中对应的缓存行是只读的,因此CPU0放置”a”的新值到它的存储缓冲中,并发送一个”无效消息”,为了刷新来至CPU1缓存的对应的缓存行.

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

3.CPU0执行b=1,它已经拥有此缓存行(也就是说,缓存行已经在”修改”或”独占”状态下),因此它存储”b”的新值到它的缓存行中.

4.CPU0接收”读”消息,并发送包含最新”b”值的缓存行到CPU1,也在它自己的缓存中将行标记为”共享”.

5.CPU1接收对于”a”的”无效消息”,放置它到它的无效队列,并发送一个”无效确认”消息到CPU0.注意:”a”的旧值仍然保留在CPU1的缓存中.

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

7.CPU1能现在完成执行while(b==0)continue;并由于它找到”b”的值为1,它继续下一条语句.

8.CPU1执行smp_mb(),标记项在它的无效队列.

9.CPU1执行assert(a==1),但由于对于包含”a”的缓存行在无效队列中有标记,CPU1必须停顿此装载直到无效队列中的项被应用.

10.CPU1处理”无效”消息,移除包含”a”的缓存行从它的缓存中.

11.CPU1现在可以自由的装载”a”的值,但由于此导致一个缓存未命中,它必须发送”读”消息去获取对应的缓存行.

12.CPU0接收来自CPU0(译者注:CPU1)对于”a”的”无效确认”消息,并因此应用缓冲存储,改变对应缓存行的MESI状态为”修改”.

13.CPU0接收来自CPU1对”a”的”读”消息,并因此改变对应缓存行的状态为”共享”,并发送缓存行到CPU1.

14.CPU1接收包含”a”的缓存行,并因此可完成装载.由于此装载返回”a”的更新值,断言通过.

有大量的MESI消息的传递,CPU得到正确的结果.

5. 读和写内存栅栏

在前面的章节,内存栅栏被同时用于标记存储缓存和无效队列的项,但在代码段中,foo()没有理由去做任何关于无效队列的事,而bar()同样没有理由去做任何关于存储队列的事.

很多CPU结构,因而提供更弱一些的内存栅栏指令,只做两者之一.大致来说,一个”读内存栅栏”仅标记无效队列,并且一个”写内存栅栏”仅标记存储缓冲.而一个完整的内存栅栏两者都做.

效果是,读内存栅栏仅排序CPU的装载并执行,因此所有在读内存栅栏之前的装载,将视为在读内存栅栏后的任何装载之前完成.类似,写内存栅栏仅排序CPU的存储,并在CPU上执行,并再一次因此所有在写内存栅栏之前的存储,将视为在写内存栅栏后的任何存储之前完成.一个完整的内存栅栏同时排序装载和存储,但仅在执行内存栅栏的CPU上.

如果更新foo和bar去使用读和写内存栅栏,显示如下:

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

一些计算机有更多种类的内存栅栏,但通常理解这三种将提供一种好的内存栅栏引导.

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

相关推荐

Qt 5.15.2静态编译避坑实录:MinGW 32位版从源码到Creator配置的全流程踩坑总结

本文详细介绍了Qt 5.15.2 MinGW 32位版本的静态编译全流程,包括环境准备、配置参数优化、编译过程监控及安装后配置。重点解决了静态编译中的常见问题,如参数顺序敏感、路径引号问题等,并提供了性能对比实测数据,帮助开发者高效完成静态编译。

weixin_26749245的博客 271

Java虚拟机之内存屏障/内存栅栏

内存屏障(Memory Barrier)也被称为内存栅栏或内存隔离墙,是一种用于控制多处理器系统中内存访问顺序的同步机制,用于防止CPU和编译器的优化行为导致内存访问顺序的改变。在多线程编程中,由于处理器和编译器可能会对指令进行重排序以优化性能,这可能会引起多线程间的同步问题。内存屏障可以用来解决这类问题,确保所有线程都能看到一致的内存状态。

Coder-thinking的博客 804

告别环境配置噩梦:用Docker Desktop + WSL2在Windows上5分钟搞定vLLM运行环境

本文详细介绍了如何在Windows系统上使用Docker Desktop和WSL2快速搭建vLLM开发环境,5分钟内完成配置。通过容器化技术解决传统环境配置中的依赖冲突、环境污染等问题,提供一键复用的高效方案,特别适合AI开发者快速部署和测试vLLM框架。

weixin_28728495的博客 503

内存栅栏与乱序执行

目录 1、内存栅栏 2、乱序执行(out-of-order execution) 1、内存栅栏 也称内存屏障,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。 语义上,内存屏障之前的所有写操作都要写入内存;内存屏障之后的读操作都可以获得同步屏障之前的写操作的结果。因此,对于敏感的程序块,写操作之后、读操作之前可以插入内存屏障。 大多数处理器提供了内存屏障指令: 完全内存屏障(f

李白 1669

Versal ACAP NoC实战:像理解计算机网络一样配置AXI NoC,打通PS与PL数据通路

本文深入探讨了Versal ACAP NoC的配置与优化,通过计算机网络类比解析NoC架构,详细介绍了在VD100开发板上配置AXI NoC实现PS与PL数据交互的实战步骤。文章涵盖NoC组件功能、Vivado配置技巧、性能优化方法及常见问题解决方案,帮助开发者高效打通Versal芯片的数据通路。

weixin_42665255的博客 294

【超详细】MIT 液态神经网络(LNNs)——深度学习新动向

输入可以是任何形式的时间序列数据,其中表示时间。例如传感器读数、金融市场数据、语音信号等。

allein_STR的博客 2613

Java并发学习笔记(四)-栅栏CyclicBarrier

闭锁是一次性对象,一旦进入终止状态,就不能被重置,它是用来启动一组相关的操作,或者等待一组相关的操作结束。 栅栏跟闭锁有点类似,它能阻塞一组线程直到某个时间发生,但是这里有个很大的区别,在栅栏里,只有这组线程都到达栅栏位置时,才能继续执行 栅栏和闭锁有以下区别 栅栏可重复使用,在计数器为0的时候,会重置为原来的计数 栅栏有栅栏动作,在计数器为0的时候,会执行栅栏动作 那么栅栏的实现机制跟闭锁有什么区别? 跟闭锁依靠Sync不同,栅栏是依靠可重入锁实现的。

ltnb的专栏 5243

YOLOv8安防监控实战:跌倒/闯入/徘徊实时检测(附TensorRT加速+声光报警部署代码)

精准性:针对跌倒/闯入/徘徊的行为特征优化,准确率达89.3%,误报率低于5次/天;实时性:TensorRT加速到50+FPS,报警延迟<1秒,满足安防“黄金响应时间”;低成本:无需专用AI芯片,RTX 3060或Jetson Orin就能跑,硬件成本比传统方案低60%;易部署:代码全开源,提供从模型训练到硬件联动的完整流程,2天即可搭起原型系统。如果你的安防监控还在“漏报误报”“响应慢”,建议先从跌倒检测的姿态过滤改起——用100张样本测试,再集成TensorRT加速,90%能看到明显改善。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 681

内存栅栏-Java

什么是内存栅栏? 内存栅栏在x86架构上如何实现的? jdk8是如何调用这个系统底层api的? 转载于:https://my.oschina.net/pypy/blog/756027...

chuqin5343的博客 255

【PX4学习笔记】1. ROS及PX4环境搭建

环境 Ubuntu18.04 安装教程参考 https://www.guyuehome.com/8983 安装过程中问题的解决 问题1 rosdep init 错误的终极解决方法 注意etc/下的ros和rostep文件夹要自己建 问题2 Ubuntu安装ROS报错 sudo: rosdep:找不到命令 问题3(更好地解决了问题1、2)【推荐】 解决GitHub的raw.githubusercontent.com无法连接问题 问题4:GeographicLib的安装 教程中安装GeographicLib d

【人工智障 | 计算鸡视觉 | 机器孩】 8234

轮式并联五连杆下肢机器人运动控制:从建模到强化学习仿真训练

本文提出了一种基于强化学习的轮式并联五连杆下肢机器人运动控制方法。首先建立了机器人的运动学和动力学模型,推导了正逆解公式,并构建了URDF模型。随后在PyBullet仿真环境中搭建了包含复杂地形的训练场景,设计了基于PPO算法的端到端控制策略,包含状态空间、动作空间和奖励函数的详细设计。通过多组仿真实验验证了该方法在台阶攀爬等任务中的有效性,为轮腿机器人运动控制提供了完整的可复现技术方案。

huanghm88的专栏 350

内存栅栏 笔记

简单来说,内存栅栏就是从本地或工作内存到主存之间的拷贝动作。 仅当系操作线程先跨越内存栅栏而读线程后跨越内存栅栏的情况下,写操作线程所做的变更才对其他线程可见。 关键字synchronized 和volatile都强制规定了所有的变更必须全局可见,该特性有助于跨越内存边界动作的发生,无论是有意为之还是无意。 在程序运行过程中,所有的变更会先在寄存器或本地cache中完成,然后才会被拷贝到主存...

yujunhua93的博客 256

深度学习时间序列预测:GRU算法构建多变量时间序列预测模型+代码实战

深度学习多变量时间序列预测:GRU算法构建多变量时间序列预测模型+代码实战 注意参考:深度学习多变量时间序列预测:GRU算法构建单变量时间序列预测模型+代码实战 GRU(Gate Recurrent Unit)是循环神经网络(Recurrent Neural Network, RNN)的一种。和LSTM(Long-Short Term Memory)一样,也是为了解决长期记忆和反向传播中的梯度等问题而提出来的。 GRU和LSTM在很多情况下实际表现上相差无几,那么为什么我们要使用新人GRU(.

data+scenario+science+insight 4033

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

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

从头来过 1009

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

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

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

从头来过 952

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

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

因翻译水平有限,如有不妥,敬请指正和谅解! 原文下载地址: http://download.youkuaiyun.com/download/programresearch/98296743.存储导致不必要的停顿  虽然显示在图1中的缓存结构,对重复的从指定的CPU读和写指定的数据项提供了好的性能,但它的性能对于第一次写一个指定的缓存行却相当糟糕.为此考虑图4,它显示了由CPU0写一个由CPU1缓存持有的缓存

从头来过 561

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

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

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

fpcc的专栏 846

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

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

Where there is life, there is hope. 2671

跟我学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

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

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

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 1995

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

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

weixin_29172201的博客 257
上一篇: 内存栅栏:软件高手的硬件观(二)
下一篇: 内存栅栏:软件高手的硬件观(四)
ProgramResearch
博客等级 码龄11年 0粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值