第七章、完事开头难吗—一切从取指令开始(1)
7.1 取指概述
7.1.1 取指特点
指令PC:每条指令在存储器空间中所处的地址
取指:处理器核将指令按照对应地址从存储器中读取出来的过程
取指模块目标:以最快速度连续不断地从存储器取出指令供处理器核执行
取指模块性能指标
- 对于非分支跳转指令,要能快速读取。即使是地址不对齐的32位指令,也最好能够连续不断地每个周期读出一条完整指令
- 对于分支跳转指令,要能快速判定是否跳转。如需跳转,则从新的PC地址处快速取出指令,即使地址不对齐地32位指令,也最好能够一个周期读出一条完整指令
7.1.2 如何快速取指
首先需要保证存储器读延迟越小越好。片外DDR存储器或者Flash存储器可能需要几十个周期的延迟,片上地SRAM也可能要几个周期的延迟。为了能快速取值,通常使用ITCM和I-Cache的方法。
ITCM(Instruction Tightly Coupled Memory)
- 指令紧耦合存储器,一般几十KB(SRAM实现)用于存储指令,物理上离处理器核很近,延迟通常一个时钟周期
- 实现简单,容易理解,能保证实时性
- 缺点是使用地址区间寻址,无法像缓存Cache那样映射无限大的存储器空间;并且为了保证低延迟,容量一般有限,只能用于存放容量大小有限的关键程序指令。
I-Cache(Instruction Cache)
- 指令缓存,是指利用软件程序的时间局部性核空间局部性,将容量巨大的外部指令存储器空间动态映射到容量有限的指令缓存中,可以将访问指令存储器的平均延迟降到最低
- 缓存不命中(Cache Miss),则需要从外部存储器中读取数据,造成较长的延迟。因此使用缓存,无法保证处理器反应的实时性。
- 对于实时性要求较高的场景,更倾向于使用延迟确定的ITCM。
7.1.3 如何处理非对齐指令
ITCM和I-Cache的存储一般用SRAM实现。SRAM读端口往往宽度固定,以32位SRAM为例,每个时钟周期可以按照一个地址读出一个32位的数据。若一条指令处于地址不对其的位置,则需要两个时钟周期进行读取再拼接。所以要想办法实现非对齐的指令也能一个周期进行读取。
普通指令非对齐:若从ITCM取出的32位指令字只用到了低16位,就可以把高16位暂存于剩余缓存,待下周期取出下一个32位指令字就可以马上进行拼接
分支跳转指令非对齐:如果跳转目标地址与32位地址边界不对齐,且需要取出32位,此时剩余缓存无法实现。可以采用两块32位SRAM交错存储,地址不对齐的指令可以在同一个时钟周期访问两个SRAM各取16位进行拼接
7.1.4 如何处理分支指令
1、分支指令类型
无条件跳转/分支
- 无条件直接跳转/分支:跳转目标从指令编码中的立即数直接获得
- 无条件间接跳转/分支:跳转目标从指令编码中的寄存器索引对应的寄存器获得
带条件跳转/分支
- 带条件直接跳转/分支
- 带条件间接跳转/分支:RISC-V架构没有此类型指令
为防止带条件跳转造成流水线空周期,采用分支预测技术。
- 预测方向:预测是否真的需要跳转
- 预测地址:如果跳转,跳转目标是什么
2、预测方向
静态预测:仅依靠这条分支指令本身的信息进行预测
- 最简单的静态预测方法是总预测分支指令不会跳转。在MIPS五级流水中,加入分支延迟槽,尽管可能预测错误造成冲刷流水线,但是分支延迟槽中的指令一定会执行,也不会造成性能浪费
- BFTN预测,对于向后的跳转预测为跳,对于向前的跳转预测为不跳。BFTN依据是通常实际汇编程序中向后跳转情形多于向前跳转情形。
动态预测:依赖已经执行过的指令的历史信息和分支指令本身的信息综合进行预测
-
最简单动态预测,一比特饱和计数器:下一次分支指令采用上次记录的跳转方向作为预测
-
两比特饱和计数器:
每次预测出错,则按反方向更改状态机状态。理想情况是对每一条分支指令配有专门两比特饱和计数器进行预测,但是实际不可行。实际采用有限个两比特饱和计数器组成一个表格,对于每条分支指令按表格索引,可能会出现不同分支指令指向同样表项的情况,称为别名重合。
- 一级预测器:采用PC值得一部分进行索引。比如采用PC值后10位,作为索引,则需维护1000个表项。
- 两级预测器:对于每条分支指令,将有限个两比特饱和计数器组织成PHT,使用该分支跳转的历史作为PHT索引。
- 局部分支预测器:使用分立得局部历史缓存保存不同指令得分支历史,每个局部历史缓存有自己对应的PHT。每条分支指令,先所引导对应的局部历史缓存,再根据历史值索引对应PHT。
- 全局分支预测器:仅使用所有得分支指令共享的全局历史缓存,不同指令可能相互冲击,但是可以节省资源。
3、预测地址
常见技术
- BTB(分支目标缓存),使用缓存保存最近执行过的分支指令得PC值以及跳转目标地址,对于要预测的指令将其PC值与缓存中的PC值进行比较,如果匹配则按存储的跳转目标进行跳转
- RAS(返回地址堆栈),使用容量有限的硬件堆栈来存储函数调用(间接跳转)的返回地址。
7.2 RISC-V架构特点对于取值的简化
7.2.1 规整的指令编码格式
取值时能尽快译码获得当前取出的指令类型
7.2.2 指令长度指示码放于低位
长度信息放于低位,如果是16位指令也可以一取得就能判断是16还是32,不必等待取完另外16指令字。而若不必支持压缩指令子集,指令低几位还可以忽略不计,减少指令缓存开销。
7.2.3 简单的分支跳转指令
- 无条件直接跳转 jal 可用于子程序调用
- 无条件间接跳转 jalr 可用于子程序返回
- 待条件直接跳转 beq、bne、blt、bltu、bge、bgeu
7.2.4 没有分支延迟槽
7.2.5 提供明确的静态分支预测依据
可以通过编译器优化代码,使向后跳转的分支指令比向前跳转的分支指令更大概率跳转,配合硬件静态分支预测,提高性能。
7.2.6 提供明确RAS依据
使用jal指令,若目标寄存器索引值rd为x1或x5,则进行RAS压栈
对于jalr指令
软件编译器按照原则生成汇编代码,可以保证硬件行为和软件行为吻合,RAS预测准确性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】