处理器可以同时进行内存和算术运算吗?

时间:2018-06-29 13:08:54

标签: c assembly cpu-architecture risc

在对汇编器和处理器的研究中,有一件事带给我如何完成指令

add mem, 1

在我的脑海中,处理器无法加载内存值 ,并且无法在同一条指令中进行算术运算。所以我认为它的发生是这样的:

mov reg, mem
add reg, 1
mov mem, reg

如果我考虑一个带有RISC Pipeline的处理器,我们会发现一些停顿。像i++这样简单的指令令人惊讶:

|  Fetch  | Decode  | Exec    | Memory  | WriteB  |
          |  Fetch  |         |         | Decode  | Exec    | Memory  | WriteB  |
                    |  Fetch  |         |         |         | Decode  | Exec    | Memory  | WriteB  |

(正如我在Patterson的《计算机体系结构:定量方法》一书中所读到的那样,寄存器是在 Decode uOp中读取,在uOp中存储/加载到内存中的,我们允许我们自己在内存uOp处获取寄存器的值。)

我是对的吗?还是现代处理器有特定的方法来更有效地做到这一点?

1 个答案:

答案 0 :(得分:6)

您是对的,现代的x86可以将add dword [mem], 1解码为3 oups:加载,ALU添加和存储。

这3个依赖操作不能同时发生,因为后面的操作必须等待前面的操作的结果。

但是独立指令的执行可能会重叠,并且现代CPU非常积极地寻找并利用“指令级并行性”来使您的代码每时钟运行速度快于1 uop。参见this answer for an intro to what a single CPU core can do in parallel,其中包含指向更多内容的链接,例如Agner Fog's x86 microarch guide,以及David Kanter撰写的SandybridgeBulldozer的文章。


但是,如果您看一下Intel的P6和Sandybridge微体系结构家族,那么商店实际上就是独立的商店地址和商店数据对象。存储地址uop不依赖于加载或ALU uop,并且可以随时将存储地址写入存储缓冲区。 (英特尔的优化手册将其称为“内存顺序缓冲区”。

为提高前端吞吐量,可以将存储地址和存储数据微指令解码为微融合对。对于add,load + alu操作也可以,因此Intel CPU可以将add dword [rdi], 1解码为2个融合域uops。 (相同的加载+添加微融合可将add eax, [rdi]解码为单个uop,因此任何“简单”的解码器都可以对其进行解码,而不仅仅是可以处理多uop指令的“复杂”解码器。这减少了前端瓶颈)。

这就是为什么add [mem], 1在Intel CPU上比inc [mem]更高效的原因,尽管inc reg的效率(但更小)却与add reg,1相同。 (inc不能对它的load + inc进行微熔丝处理,这将设置标志与add不同)。 INC instruction vs ADD 1: Does it matter?

但这只是帮助前端更快地进入调度程序中。负载仍然必须与添加项分开运行。

但是微融合负载不必等待整个指令输入的其余部分准备就绪。考虑一个像add [rdi], eax这样的指令,其中RDI和EAX都是指令的输入,但是直到ALU添加uop才需要EAX。加载地址准备好并且有可用的加载执行单元(AGU +缓存访问)后,加载即可立即执行。另请参见How are x86 uops scheduled, exactly?


  

在解码uOp中读取寄存器,在内存uOp中存储/加载,我们允许我们在内存uOp中获取寄存器的值

所有当前的x86微体系结构都使用乱序执行和寄存器重命名(Tomasulo算法)。指令被重命名并发布到内核的乱序部分(ROB和调度程序)。

直到将指令从调度程序“分派”到执行单元,才读取物理寄存器文件。 (或者用于最近生成的输入,从其他uops转发。)


独立指令可以与它们的执行重叠。例如,Skylake CPU每个时钟可以维持4个融合域/ 7个非融合域uops的吞吐量,包括2个负载+ 1个存储区,in a carefully crafted loop

.loop: ; HSW: 1.12c / iter. SKL: 1.0001c
    add edx, [rsp]           ; 1 fused-domain uop:  micro-fused load+add
    mov [rax], edi           : 1 fused-domain uop:  micro-fused store-address+store-data
    blsi ebx, [rdi]          : 1 fused-domain uop:  micro-fused load+bit-manip

    dec ecx
    jnz .loop                ; 1 fused-domain uop: macro-fused dec+branch runs on port 6

桑迪布里奇(Sandybridge)系列CPU具有一个L1d高速缓存,每个时钟能够进行2次读取+ 1次写入。 (不过,在Haswell之前,只有256位向量可以在AGU吞吐量限制附近工作。请参见How can cache be that fast?。)

桑迪布里奇(Sandybridge)家庭的前端吞吐量是每个时钟4个融合域uops,并且它们的后端有很多执行单元来处理各种指令混合。 (Haswell及其以后的版本具有4个整数ALU,2个加载端口,一个存储数据端口和一个专用的存储AGU,用于简单的存储寻址模式。因此,它们通常可以在执行高速缓存未命中停止执行后迅速“追赶”,从而使乱序窗口中找到更多工作要做。)