MIPS架构深入理解10-向MIPS移植软件之内存序

简介: MIPS架构深入理解10-向MIPS移植软件之内存序
  • 1 内存访问的排序和重新排序
  • 2 访存顺序和写缓存
  • 3 写缓存的flush

站在巨人的肩膀上,才能看得更远。

If I have seen further, it is by standing on the shoulders of giants.


牛顿


这是向MIPS架构移植软件的问题系列之第三篇。在前两篇文章

*《MIPS架构深入理解8-向MIPS架构移植软件之大小端问题》

*《MIPS架构深入理解9-向MIPS移植软件之Cache管理》

中,我们分别讨论了大小端模式和Cache对于移植代码的影响。那么本文,我们再从内存序理解一下对于移植代码的影响,尤指底层代码或操作系统代码。


1 内存访问的排序和重新排序


程序员往往认为他们的代码是顺序执行的:CPU执行指令,更新系统的状态,然后继续下一条指令。但是,如果允许CPU乱序执行,而不是这种串行方式执行,效率可能更高。这对于执行load和store这种存储指令尤其重要。

从CPU的角度来看,执行store操作就是发送一个write请求:给出内存地址和数据,其余的交给内存控制器完成。实际的内存和I/O设备相对较慢,等write操作完成,CPU可能已经完成了几十条甚至几百条指令。

read操作又有不同:它需要发送一个read请求,然后等待对请求的响应。当CPU需要知道内存或者设备寄存器中的内容时,没有得到请求响应前,可能啥也做不了。

如果想要追求更高的性能,就意味着我们需要让read尽可能地快,甚至不惜让write操作变得更慢。进一步考虑,我们可以让write操作排队等待,把随后的任何read操作请求提前到write请求队列之前执行。从CPU的角度来看,这是一个大优势:尽可能快地启动read操作,就越早得到read操作的响应。然后,在某个时刻把执行write操作,而且write请求队列的大小是固定的。但是,这个write操作可能需要写Cache一段时间。如果这个队列满了,可能需要停下来等待一段时间,等待所有的write完成操作。但是,这肯定要比顺序执行,效率更高。这就是现代CPU一般都具有一个write buffer的原因。

看到这儿,你可能会有一个疑问:某些程序可能会写入一个地址,然后再将其读回来,这时候会怎么样呢?如果read提前到write之前执行,我们可能从内存中读取的是旧值,从而导致程序发生故障。通常,CPU会提供额外的硬件,比较read操作的地址和write队列中的地址,如果有相同的项,就不允许这样的read操作提前到write操作之前执行。

上面的讨论没有考虑真正的并发系统,比如多核系统。并发执行的任务间共享变量,对其执行read和write操作会非常危险。比如使用共享变量进行同步和通信的时候,内存访问次序就会非常重要。这种情况下,软件一般会采用精心的设计,比如锁和信号量,进行同步操作。

但是,使用共享内存,还有一些技巧,往往效果更好,开销也更小。因为不需要使用信号量或者锁。但是,可能会被乱序执行打断。假设,我们有2个任务,如下图所示:一个读取数据结构,一个写数据结构。它们可以交替使用这个数据结构。


640.png

为了能够正确执行,我们需要知道,对于reader任务来说,当什么时候reader任务看见关键域中的值发生了更新时,能够保证其它所有的更新对reader任务可见。

当然,硬件可以实现所有的内存访问顺序问题,从而将它们对程序员不可见,但是也就放弃了解耦read和write操作带来的性能优势。MIPS架构提供了sync指令实现这个目的,它可以确保sync指令之前的访问先于之后的执行。但是,这种保障指令有其局限性:只与内存的访问顺序有关,只能被非Cache或具有Cache一致性的内存访问的参与者看见。

对于上面的示例,为了让其在合适的系统上可靠地运行,writer任务应该在写关键域的值之前,调用一条sync指令;reader任务应该在读关键域的值之后插入一条sync指令。对于sync指令的详细使用方法,可以参考《MIPS指令集参考大全》一文。

不同的体系架构对执行顺序作出了不同的规定。一类极端情况就是,要求所有的CPU和系统设计人员努力保证一个CPU的全部读和写操作,从另一个CPU的角度看上去顺序完全相同,这叫做强序。也有一类情况就是弱序,比如只要求所有的写操作保持顺序不变。而MIPS架构更为激进,完全就是无序访问内存。这就要求我们系统开发人员必须手动保证内存的访问顺序是正确的。


2 访存顺序和写缓存


前面讨论了这么多理论,接下来让我们讨论点实际的内容吧。把write操作缓存到一个队列中(也就是硬件中常常讨论的write buffer)的思想在实践中证明非常有效。因为,store指令往往是多条指令扎堆出现。比如,一个运行MIPS代码的CPU,实际上运行的store指令大约占所有指令的10%左右;但是,往往是突发式访问,比如函数的调用过程中,首先需要压栈操作一组寄存器的值。

但是,一般情况下,写缓存(英文称为write buffer)都是硬件保证的,对于软件来说不用管理。但是,也有一些特殊的情况,程序员需要知道怎样处理:

  1. I/O寄存器访问的时序
    这个问题,对于所有架构CPU都存在。比如,CPU发出一个store指令,更新I/O设备寄存器的值,write请求可能会在写缓存中延迟一段时间。这时候,可能会发生其它事件,比如中断。但是此时写入的值还未更新到对应的I/O设备寄存器中。这可能导致一些奇怪的行为:比如,你想禁止产生中断,但是CPU发出write操作之后,CPU还有可能会收到中断。
  2. read操作抢先于write操作执行
    上面已经讨论过,MIPS32/64架构允许这种操作。如果想要软件更加健壮和具有可移植性,就不应该假定read和write操作顺序会被保持。如果想要保证前后两个指令周期是按照特定顺序执行,就需要插入sync指令。
  3. 字节汇集
    有些写缓存会汇集不足WORD大小的write操作,凑成一个WORD大小的write操作,然后再执行(有些写缓存甚至会攒一个Cache行,然后再写入)。所以,为了避免对于非Cache的内存区也做相同的操作,最好的办法就是把I/O寄存器(比如,一个8位的寄存器)映射到一个单独的WORD大小的地址上。


3 写缓存的flush


通过对非Cache内存区的任意位置执行write操作,然后再read,可以清空写缓存(大部分都是这样实现的)。当然,写缓存不允许read操作发生在write之前,这样导致返回旧值。所以,必须在write和read操作之间,插入sync指令。对于兼容MIPS32/64规范的任何系统,这应该都是有效的。

但是,有效不等于高效。通过提高内存的读写速度也可以降低整体的负荷。有些特定的系统可能会提供更快的内存或者写缓存。

任何具有回写功能的处理器或者内存接口,都引入了写缓存。只是,有的在CPU内部实现,有的在CPU外部实现。不管是在CPU内部,还是在CPU外部,麻烦是相同的。在编程的时候,一定要仔细确认你的系统中,写缓存的位置,善加利用。

相关文章
|
5月前
|
存储 机器学习/深度学习 缓存
软考软件评测师——计算机组成与体系结构(分级存储架构)
本内容全面解析了计算机存储系统的四大核心领域:虚拟存储技术、局部性原理、分级存储体系架构及存储器类型。虚拟存储通过软硬件协同扩展内存,支持动态加载与地址转换;局部性原理揭示程序运行特性,指导缓存设计优化;分级存储架构从寄存器到外存逐级扩展,平衡速度、容量与成本;存储器类型按寻址和访问方式分类,并介绍新型存储技术。最后探讨了存储系统未来优化趋势,如异构集成、智能预取和近存储计算等,为突破性能瓶颈提供了新方向。
|
8月前
|
资源调度 监控 调度
基于SCA的软件无线电系统的概念与架构
软件通信体系架构(SCA)是基于软件定义无线电(SDR)思想构建的开放式、标准化和模块化平台,旨在通过软件实现通信功能的灵活配置。SCA起源于美军为解决“信息烟囱”问题而推出的联合战术无线电系统(JTRS),其核心目标是提升多军种联合作战通信能力。 上海介方信息公司的OpenSCA操作环境严格遵循SCA4.1/SRTF标准,支持高集成、嵌入式等场景,适用于军用通信、雷达等领域。 SCA体系包括目标平台资源层(TRL)、环境抽象层(EAL)、SRTF操作环境(OE)及应用层(AL)。其中,SRTF操作环境包含操作系统、运行时环境(RTE)和核心框架(CF),提供波形管理、资源调度等功能。
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
160 2
|
5月前
|
运维 监控 数据可视化
一文详解:工业软件“低代码开发平台”技术架构研究与分析
本文围绕工业软件低代码开发平台的机遇与挑战,提出基于自动化引擎的技术架构,由工具链、引擎库、模型库、组件库、工业数据网关和应用门户组成。文章分析了其在快速开发、传统系统升级中的应用模式及价值,如缩短创新周期、降低试错成本、解决资源缺乏和提升创新可复制性,为我国工业软件产业发展提供参考和支持。
|
7月前
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
存储 前端开发 Java
Android MVVM架构模式下如何避免内存泄漏
Android采用MVVM架构开发项目,如何避免内存泄漏风险?怎样避免内存泄漏?
366 1
|
7月前
|
存储 人工智能 自然语言处理
Cursor这类编程Agent软件的模型架构与工作流程
编程Agent的核心是一个强大的大语言模型,负责理解用户意图并生成相应的代码和解决方案。这些模型通过海量文本和代码数据的训练,掌握了广泛的编程知识和语言理解能力。
723 1
|
运维 负载均衡 Shell
控制员工上网软件:高可用架构的构建方法
本文介绍了构建控制员工上网软件的高可用架构的方法,包括负载均衡、数据备份与恢复、故障检测与自动切换等关键机制,以确保企业网络管理系统的稳定运行。通过具体代码示例,展示了如何实现这些机制。
237 63
|
10月前
|
存储 缓存 关系型数据库
社交软件红包技术解密(六):微信红包系统的存储层架构演进实践
微信红包本质是小额资金在用户帐户流转,有发、抢、拆三大步骤。在这个过程中对事务有高要求,所以订单最终要基于传统的RDBMS,这方面是它的强项,最终订单的存储使用互联网行业最通用的MySQL数据库。支持事务、成熟稳定,我们的团队在MySQL上有长期技术积累。但是传统数据库的扩展性有局限,需要通过架构解决。
271 18
|
监控 前端开发 数据可视化
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
@icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
774 9
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生

热门文章

最新文章

下一篇
oss云网关配置