《深入理解计算机系统》自学历程(一)模拟高速缓存逻辑(上)
《深入理解计算机系统》笔记
一、首先复习一下
存储越大寻址时间越慢、效率越低,虽然相对来说每一个数据计算机都会用到,但在某一阶段、某一个特定时间,使用到的数据范围是相对固定的。
处理器需要更快速的处理速度,需要快速得到指令和数据,而这些指令和数据都是存在低级的存储中(硬盘等本地存储或网络存储),单纯拿硬盘来说,读取时间就包括了传输时间、旋转时间、寻道时间,效率太低。为了更快速的让处理器得到数据,更好的利用处理器的性能,现代处理器演化出了分支预测的功能单元,可以让处理器在未执行到某一个分支之前就通过投机先行计算(不在本次范围内)。另外,除了内部的文件寄存器,计算机将处理器和主存之间增加了多级缓存,用来存放处理器需要用到的指令和数据。因此,处理器在活动时可以通过分支预测等技术同步的将后续将会使用到的指令和数据加载到缓存中(指令高速缓存/数据高速缓存),可以让整个处理器流水线的效率更高。
注:L0,L1,L2,L3级缓存的效率都是成百倍的降低,L4主存的效率相比L1更是天差地别。客观条件相同的情况下,存储越大寻址速度越慢。虽然说从材料上面来说也有不同,不单单是大小的问题,但材料方面不在本次讨论范围内。
二、缓存
缓存是低层次存储的缓存,或者说是对低层次缓存的“凝练和融合”(L1是L2的缓存,L2是L3的缓存)。通过建立一套规则和逻辑,我们将低层次存储中的数据根据需要读取到高层次缓存中。
本次说到的高速缓存的结构划分为以下几个部分(概念)
组 行 数据块
S E block
一个高速缓存的大小(C)就是 S*E*B(blockSize) 其中 B S 必须是2的N次幂
2.1 为什么要这么划分?
我们需要一套机制,将内存地址与高速缓存的索引之间建立关系,上面说的方式就是其中一种。
S代表了高速缓存的组数,E代表了每组中的行数,B代表每行中的数据块大小,通过这S值和B值,我们就可以对内存地址进行掩码处理,并得到相应的组值和数据块偏移。
简单分步骤的说:
1)计算机中的信息是用二进制表示的。
2)S组都是 2的N次幂,代表他们都是通过范围的2进制位数表示的(如果S=5,那么值就可能是0,1,10,11,100,101;但三位二进数还有110、111的值,就无法用三位截取地址的方式来计算组索引了)
3)B数据块也是2的N次幂,原因同S。
所以 s = log2(S) 就是 组索引在地址中的位数,b = log2(B)就是数据块偏移的位数。
1000 1000 1000 1000
t s b
2.2 t是什么?
t代表标识,设想一下,既然是缓存,那么就是将低级存储进行缓存,大小一定是比低级存储更小,所以速度更快。更小代表着较多的数据要使用较小的位置,也就是说多个地址的数据使用一个缓存位置来存数(覆盖什么规则后期再谈),那么久需要一个标识来表示这个缓存单元中的数到底是哪个内存的,否则读取数据的时候都不知道该读哪个了。
t的公式是 memAddrLength – b – s。就是说,去掉组索引位和数据块偏移位,剩下的就是标识位了。
2.3 结果呢,总体来说高速缓存到底什么样子?
通过组和行我们把高速缓存划分成了一个树形结构,当需要读取低级存储的时候,先将地址根据t s b的规则进行拆分,通过s找到相应的组(组数*行数*高速缓存单元的大小),再通过标识找到对应的行,然后根据块偏移把数据读出来。总的来说就是这么简单。
实际上呢还有不少细节的问题,比如如果不命中怎么办?如果命中的时候对应的高速缓存单元还没热身怎么办(没读取数据)?如果现在内存地址对应的组都已经写满了应该替换哪一个? 等等,这些计算机现在都有相应的策略,感兴趣的同学可以去看看书。
3.我要做什么
我准备用C语言自己写一个高速缓存的机制试试,首先我没有低级存储(目前还不会硬件),低级存储是用一个指针加一部分内存空间抽象的。而高速缓存也一样,也在内存里,用一个指针加一部分内存空间抽象的。写这个的目的是为了让自己更好的理解高速缓存的原理。
实现的功能是:
1)实现高速缓存读功能,可以根据内存地址进行掩码处理得到对应的t s b值并从“高速缓存空间”中获取到高速缓存单元。
2)实现高速缓存的写功能,现在计划使用直写的方式,即直接写入到“低级存储”中不根据高速缓存的写状态进行处理。
目前进行了组件设计和活动图设计,图如下:
1)组件图:
2)交互图
3)读活动图
4)写活动图
代码后续会放到 git@code.aliyun.com:qdxiayongming/C.learn.git
望有有经验的朋友指正。