笔记|组成原理:原理篇(冒险和预测)
2021-07-02 20:28:57 0 举报
徐大的组成原理课
作者其他创作
大纲/内容
五级流水线:取指令(IF)指令译码(ID)指令执行(EX)内存访问(MEM)数据写回(WB)
流水线停顿Stall/流水线冒泡Pipeline Bubbling
增加资源:指令缓存、数据缓存
int main() { int a = 1; int b = 2; a = b+a; b = a + b;}
存在问题:没有办法根据实际的应用去动态分配。虽然解决了资源冲突的问题,但也失去了灵活性。
分支预测失败
eg2:薄膜键盘的“锁键”问题(机械键盘√)
本质:在硬件电路层面,把一些计算结果更早的反馈到流水线中
分类
增加资源
用更多的信息,而不是一次的分支信息来进行预测
1. 在取指令和指令译码的时候,乱序执行的 CPU 和其他使用流水线架构的 CPU 是一样的。它会一级一级顺序地进行取指令和指令译码的工作。2. 在指令译码完成之后,就不一样了。CPU 不会直接进行指令执行,而是进行一次指令分发,把指令发到一个叫作保留站(Reservation Stations)的地方。顾名思义,这个保留站,就像一个火车站一样。发送到车站的指令,就像是一列列的火车。3. 这些指令不会立刻执行,而要等待它们所依赖的数据,传递给它们之后才会执行。这就好像一列列的火车都要等到乘客来齐了才能出发。4. 一旦指令依赖的数据来齐了,指令就可以交到后面的功能单元(Function Unit,FU),其实就是 ALU,去执行了。我们有很多功能单元可以并行运行,但是不同的功能单元能够支持执行的指令并不相同。就和我们的铁轨一样,有些从上海北上,可以到北京和哈尔滨;有些是南下的,可以到广州和深圳。5. 指令执行的阶段完成之后,我们并不能立刻把结果写回到寄存器里面去,而是把结果再存放到一个叫作重排序缓冲区(Re-Order Buffer,ROB)的地方。6. 在重排序缓冲区里,我们的 CPU 会按照取指令的顺序,对指令的计算结果重新排序。只有排在前面的指令都已经完成了,才会提交指令,完成整个指令的运算结果。7. 实际的指令的计算结果数据,并不是直接写到内存或者高速缓存里,而是先写入存储缓冲区(Store Buffer 面,最终才会写入到高速缓存和内存里。
而内存只有一个地址译码器作为输入
STORE:将数据从寄存器写入内存【没有数据写回的流水线截断】
先读后写
操作数前推
LOAD:从内存里读取数据【需要经历5个完整的流水线】
控制冒险
中间件: 指令缓存、数据缓存
以牺牲CPU性能为代价
如何通过流水线设计提高CPU的吞吐率,需要冒哪些风险(Hazard)
解决:NOP操作进行对齐
乱序执行
再等等:通过流水线停顿解决数据冒险
CPU在同一个时钟周期内,同时在运行两条计算指令的不同阶段,可能会用到同样的硬件电路
int main() { int a = 1; int b = 2; a = a + 2; b = a + 3;}
分支预测
先写后读
NOP操作和指令对齐
原理篇:处理器
指令执行阶段开始(流水线前两个阶段:取指令、译码不需要停顿)
在内存地址为12的机器码,我们把0x2添加到rbp-0x4对应的内存地址里面。然后,在紧接着的内存地址为16的机器码,我们又要从rbp-0x4的这个内存地址里面,把数据写入到eax这个寄存器里面。【保证:在内存地址为16的指令读取rbp-0x4里面的值之前,内存地址12的指令写入到rbp-0x4的操作必须完成】
结果
写后再写
定义:同时在执行的多个指令之间,有三种不同的数据依赖的情况
流水线停顿
本质:程序逻辑层面的问题
直接进行等待:插入NOP这样的无效指令
结构冒险 & 数据冒险
解决方案:把内存分为两部分,让他们各自有各自的地址译码器。 【存放指令的程序内存 & 存放数据的数据内存】
结构冒险
分支预测成功
动态分支预测
实现:将条件判断、地址跳转,都提前到指令译码阶段进行,而不需要放在指令执行阶段。对应的,我们也要在CPU里面设计对应的旁路,在指令译码阶段,就提供对应的判断比较电路
缩短分支延迟
”全键无冲“的资源解决方案本质:增加资源(~人手≠真正的高效率)
在内存地址为15的汇编指令里,我们要把eax寄存器里面的值读出来,再加到rbp-0x4的内存地址里。接着在内存地址为18的汇编指令里,我们要再写入更新eax寄存器里面。如果在内存地址18的eax的写入先完成了,在内存地址为15的代码里面取出的eax才发生,我们的程序计算就会出错。
eg:内存的数据访问
数据冒险
结构冒险数据冒险
仍然沿用
静态分支预测
引入状态机
输出依赖
避免了结构冒险
数据依赖
把后面已经取出指令已经执行的部分丢掉:【Zap / Flush】
CPU里的线程池与乱序执行
⭐普林斯顿结构/冯诺依曼结构
本质:在指令执行的截断通过一个类似线程池的保留站,让系统自己去动态调度先执行哪些指令。指令执行的先后顺序不再和他们在程序中的顺序有关。只要保证不破坏数据依赖就好了
假装分支不发生,CPU预测条件跳转一定不发生
int main() { int a = 1; a = 2;}
访存和取指令,都要进行内存数据的读取。
冒险和预测总结
反依赖
同一个时钟周期,两个不同指令访问同一个资源
本质:硬件层面的资源竞争问题(硬件电路层面问题)
现代CPU架构:借鉴了哈佛结构,在高速缓存层面拆分成指令缓存、数据缓存
后面执行的指令会对前面执行的指令有数据层面的依赖关系
ADD/SUB:在寄存器内部完成【没有实际的内存访问操作MEM】
⭐哈佛结构
0 条评论
回复 删除
下一页