微架构是指令集架构的一种实现或者设计.
1. fetch
从内存中获取指令
2. decode
将获取的指令解码为 uOp
3. excute
执行指令
4. write back
存储计算结果到 寄存器 或者 内存
将4个阶段中的每个阶段都扩展一下
将fetch 扩展为 5个阶段
...
...
...
然后就形成了 深度为20的pipeline
管道阶段越多,每个阶段的专业化(设计越简单,更快)程度就越高,相对的也就越快,那么整个全部流程会越快
现代处理器大约有15-20个阶段
获取和解码阶段通常涉及到 6-10 个阶段,被称为微处理器的前端
执行和写回阶段也大约6-10个阶段,被称为 后端
cpu的流水线是同步的
// 预测属于 fetch 阶段
1.遇到分支指令如何预取下一条指令 // 遇到分支指令还会继续预取,而不是停下来等到分支指令的执行后再预取
2.分支指令可能会将流水线中的指令给清空
意思是 我们会预取一些不需要执行的指令
如果我们能将这个预取的不需要指令的指令数目降低,那么就会提高整体的运行速度.
现代处理器 会 根据预测 预取 // speculative execution
1.分支指令A执行后,会更新一些部件B
2.下次该分支指令A预取后, 这些部件B会选择 指令A 后的 其中一条路径的指令 来预取
分支预测器(只有一个)
能够记录 1000个分支的预测方向
预取器(只有一个)
预取指令
cache
1-2 clocks
如果cache 为空 , 就会批量预取相邻指令到 cache 中,然后将他们交给解码器(解码器有很多个)
CPU管道前端的主要目标是确保使用有足够的指令可供后端执行
,避免因等待内存中的指令字节而出现空闲时间,
或因错误的分支预测导致获取的指令最终被丢弃,
因此浪费时间
前端的第二部分是将程序指令解码为 微架构的内部操作(微操作Micro-Operations,简称uOps)
uOps 是指令集架构与微架构之间最牢固的联系
指令与 uOp的对应关系 比例
1:1 绝大多数
n(相邻指令):1 少数
1:n 少数
前端的工作始终是研究如何解码 和 准备这些指令,以确保高效执行
有些微架构会创建解码缓存,以便 将来遇到时,直接映射.
对指令进行解码,或从解码缓存中读取指令之后,uOp将被传递到管道后端
superscalar execution
简单的执行形式是算术逻辑单元,ALU(用来执行加减运算)
如果只有一个ALU,一次则只能执行一次加法运算,被称作标量执行
现代处理器可以有多个ALU,ALU并行计算,即超标量执行
可以并行执行的运算量(有多少个ALU)是衡量微处理器宽度的方法之一.
所有现代微处理器都属于超标量微处理器
一个核心有1个预取器,6个ALU,宽度为6.
我把一个soc 中集成 4个核心, 宽度(超标量能力)就是 24 , 这个也叫作多流水线
我把一个soc 中继承 1个核心,并改造该核心为24个ALU,宽度(超标量能力)就是 24,这个还是单流水线
out of order
基本微架构中, uOp 按照编程顺序执行,被称作 In-Order Execution
但是,为了提供最佳的性能和参数, 乱序执行 效果更好
这样可以消除不必要的等待时间,从而加快代码执行
但是会导致 编码中A先B后,且B依赖A, uOpB先执行uOpA后执行的情况
乱序执行的第一步
后端乱选执行的第一步是获取前端提供的uOp,并确定他们的依赖关系
如果有依赖关系,则改关系会通过一个名为"寄存器重命名Rename"的流程进行跟踪
第二步 Allocation
uOp 信息会别写到一个 "重排序缓冲区 Reorder Buffer"(ROB)的结构中,这个过程叫做 Allocation(分配)
虽然指令采用乱序执行,我们仍然需要一种方法让他们恢复顺序,就依赖 "重排序缓冲区"(ROB)
"重排序缓冲区"(ROB) 能让我们连接原始编程顺序
经过这两步的uOp 再
经过 Scheduler 把uOp 放到 可执行特定运算的执行单元(ALU或者JMP) 中
同一个ALU可能有多个实例,ALU还有不同的类型,可以执行不同类型的运算
有些可以执行整数运算
有些可以执行浮点运算或基于十进制或小数的数学运算
有些可以同时对多个元素执行运算,这些匀速叫做矢量
分支操作也有自己的执行单元JMP
Scheduler 负责理清依赖关系
当某个uOp执行完毕并成为后端中(一批次)最晚的uOp时,ROB即确定可由安全的将这个uOp写回,这个过程被称为 Retirement ,(隐退)
我们可以通过并行,乱序执行其中的很多指令,从而提高cpu的性能,同时仍能做到保持响应的依赖关系,最终,我们会得到正确的答案和正确的结果.
执行的时候还可能访存,如果执行的uOp 说 操作数 在内存中, 就需要访存