Caching 为什么从缓冲区加载/存储需要一个完整的时钟周期?
在本视频中: 28:00,他开始解释以下示例:Caching 为什么从缓冲区加载/存储需要一个完整的时钟周期?,caching,assembly,parallel-processing,cpu,clock,Caching,Assembly,Parallel Processing,Cpu,Clock,在本视频中: 28:00,他开始解释以下示例: ld rax <- [rbx+16] //Clock 0 Starts add rbx,16 cmp rax,0 jeq null_chk st [rbx-16] <- rcx //Clock 1 Starts ld rcx <- [rdx+0] //Clock 2 Starts. Why clock 1 only does one op? ld rax <- [rax+8] //Clock 3 Star
ld rax <- [rbx+16] //Clock 0 Starts
add rbx,16
cmp rax,0
jeq null_chk
st [rbx-16] <- rcx //Clock 1 Starts
ld rcx <- [rdx+0] //Clock 2 Starts. Why clock 1 only does one op?
ld rax <- [rax+8] //Clock 3 Starts. Why?
ld-rax不,他说有两个缓存未命中并行运行
这里有4次内存访问(数据内存,但我们忽略代码/页面漫游等):
地址[rbx+16]
的第一个ld可以被分派并发送到内存中
该存储只有在退休后才能执行,因为他知道它肯定是非伪造的(他提到分支可能被错误预测——这在退休前的执行时间可以知道,但可能还有其他他没有提到的情况,如故障或异常)。此外,存储地址需要等待计算add rbx,16
,但这不会花费太长时间。但是,保存存储的分支的执行和最终失效取决于rax
compare&jmp的结果,而这又取决于第一次加载(此处假定为进入内存)(换句话说,不要屏住呼吸)
第二次加载(来自[rdx+0]
)是独立的,可以调度-这是并行完成的第二次加载
第三次加载无法调度-其地址取决于rax
,这就像cmp+jmp必须等待第一次加载完成一样。这意味着我们无法处理这个负载,我们甚至不知道实际地址。例如,考虑如果负载返回,写入rax
,并且[rax+8]
等于[rbx-16]
,在硬件中会发生什么?您必须从存储转发数据
因此,在执行代码时,我们可以立即(使用1-2个周期,取决于解码和地址计算时间)发送2个加载—第1和第3节。其他内存操作将不得不等待。性能的关键因素是尽早启动加载,并尽可能并行启动。感谢您对缓存未命中的解释!最后一个问题,为什么时钟1、2和3只有一个操作,而时钟0只有4个操作?这有点过于简单化了,但假设每一条指令都可以在1个周期(当然不包括内存延迟)内解码和“启动”(基本上分配到OOO执行引擎)——他假设机器可以并行执行4条指令(查找,但还有另一个限制-每个周期不超过一条加载/存储指令,因此最后3条指令必须一个接一个地执行。与这里的内存延迟相比,它仍然可以忽略不计。真正的CPU经常被如此小的限制所困扰。