经典处理器架构的流水线是5级流水线,分别是取指、译码、执行、数据内存访问和写回。
现代处理器在设计上都采用了超标量架构(superscalar architecture)和乱序(Out-of-Order OoO)执行技术,极大地提高了处理器计算能力。超标量技术能够在一个时钟周期内执行多条 指令,实现指令级的并行,有效提高指令级的并行效率 (Instruction Level Parallelism,ILP), 同时增加整个高速缓存和内存层次结构的实现难度。
一条存储-读-写指令的执行全过程很难用一句话来扣描述。在一个支持超标量和乱序执行技术的处理器当中,把一条存储-读-写指令的执行过程分解为若干步骤。
指令首先进入流水(pipeline)的前端(front-end),包括预取(fetch)和译码(decode),经过分发(dispatch)和调度(schedule)后进入执行单元,最后提交执行结果。所有的指令采用顺序方式通过前端,采用乱序的方式进行发射,然后乱序执行,最后用顺序方式提交执行结果,并将最终结果更新到加载-存储队列(Load-Store Queue,LSQ)单元。
LSQ单元是指令流水线的一个执行部件以理解为存储子系统的最高层,它接收来自 CPU的存储器指令,并接着存储器子系统。要功能是将来自CPU存储器请求发送到存储器子系统,并处理其下存储器子系统的应答数据和消息。
很多程序员对乱序执行的理解有误差。对于一串给定的指令序列,为了提高效率,处理器会找出非真正数据依赖和地址依赖的指令,让它们并行执行。但是在提交执行结果时,是按照指令次序提交的。总的来说,顺序提交指令,然后乱序执行,最后顺序提交执行结果。如果有两条没有数据依赖的数据指令,那么后面那条指令读的数据先返回,它的结果也不能先写回最终寄存器,而必须等到前一条指令完成之后才可以。
对于读指令,当处理器在等待数据从缓存或者内存返回时,它处于什么状态呢?是停顿,还是继续执行别的指令?对于乱序执行的处理器,可以执行后面的指令;对于顺序执行的处理器,会使流水线停顿,直到读取的数据返回。
如图所示,在Cortex-A9处理器中:
- 存储指令首先通过主存储器或者L2高速缓存加载到L1指令高速缓存中,通过总线接口单元(BIU)连接到主存储器。
- 在指令预取阶段(instruction prefetch stage),主要做指令预取和分支预测,然后指令通过指令队列和预测队列送到译码器,进行指令的译码工作。
- 译码器(decoder)支持两路译码,可以同时译码两条指令。
- 在寄存器重命名阶段(register rename stage)会做寄存器重命名,避免指令进行不必要的顺序化操作,提高处理器的指令级并行能力。
- 在指令分发阶段(dispatch stage),这里支持4路猜测发射和乱序执行(Out-of-Order Multi-lssue with Speculation),因此它支持基于推测的乱序的发射功能。
- 然后在执行单元(ALU/MUL/FPU/NEON)中乱序执行指令,最终的计算结果会在乱序写回阶段写入寄存器中。
- 存储指令会计算有效地址并将其发送到内存系统中的加载存储单元(Load Store Unit,LSU),最终LSU会访问高速缓存或主存储器。
在ARM中,只有可缓存的内存地址才需要访问高速缓存。在多处理器环境下,还需要考虑高速缓存的一致性问题。L1和 L2 高速缓存控制器需要保证高速缓存的一致性,在Cortex-A9中,高速缓存的一致性是由MESI协议来实现的。Cortex-A9处理器内置了一级缓存模块,由窥探控制单元(Snoop Control Unit,SCU)来实现高速缓存的一致性管理。L2高速缓存需要外接芯片(如 PL310)。在最糟糕的情况下需要访问主存储器,并将数据重新传递给 LSQ,完成一次存储器读写的全过程。
涉及计算机体系结构中的众多术语比较晦涩难懂,现在对部分术语做简单解释。
- 超标量架构:早期的单发射架构微处理器的流水线设计目标是做到平均每个时钟周期能执行一条指令,但这一目标不能满足提高处理器性能的要求。为了提高处理器的性能,处理器要具有每个时钟周期发射执行多条指令的能力。超标量体系结构可描述一种微处理器设计理念,它能够让处理器在一个时钟周期执行多条指令。
- 乱序执行:CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元的技术,避免处理器在计算对象不可获取时等待,从而导致流水线停顿。
- 寄存器重命名:现代处理器中的一种技术,用于避免机器指令或者微操作不必要的顺序化执行(,提高处理器的指令级并行能力。它在乱序执行的流水线中有两个作用一消除指令之间的寄存器读后写相关(Write-after-Read,WAR)和写后写相关(Write-after-Write,WAW),当指令执行发生例外或者转移指令猜测错误而取消后面的指令时,可用于保证现场的精确性。当一条指令要把内容写入一个结果寄存器时不直接写入这个结果寄存器,而是先写入一个中间寄存器进行过渡,当这条指令提交时再写入结果寄存器。
如对一个寄存器先读后写,需要顺序执行;通过寄存器重命名,可以不用顺序化执行。
WAR
R1 = R2 + R3 ----> R1 = R2 + R3
R2 = R4 + R5 ----> R6 = R4 + R5
- 调度器:负责把指令或微操作指令分发到相对应的执行单元执行,例如,Cortex-A9 处理器的调度器单元通过4个接口和执行单元连接,因此每个时钟周期可以同时分发4条指令。
- ALU:处理器的执行单元,主要包括进行算术运算、逻辑运算和关系运算的部件。
- LSQ/LSU:指令流水线的一个执行部件,其主要功能是将来自CPU的存储器请求发送到存储器子系统,并处理其下存储器子系统的应答数据和消息。