这里对常见链表这一概念的提出主要是考虑到链表有以下类型,如果每次学习时都需要重新去写,那未免太过要求高了,所以在这里提出。
1、是否有头结点head
2、是否是双向链表prev和next,这使得其难度有了一些增加,但这个的增加反而不复杂,毕竟,本质上的操作是一样的
3、将1和2进行组合,老实讲,这有一些难度了,我认为甚至可以说十分困难也不为过
4、循环链表,老实说难得不在于他的思路,而在于他的细节,每次思考都要相半天,十分繁琐,希望找找看有没有好的方法。
首先我们得知道,有一个东西叫头指针,它指向的是链表中第一个元素,只有指针才具有指向这样的说法。然后我们可以由头节点,节点不是一个箭头,而是一个节点,而有无头结点的区别就是有没有第一个不储存元素且不被当作节点使用的节点(这个就叫头结点)。
然后我们可以对头指针进行一些变换。然后我们将第一个存储数据的节点叫做首元节点。这样关系就可以了。而头指针指向的是第一个节点(而非首元节点)。
没有头指针的一个缺点是我们需要判断头指针是否为NULL,这样导致了操作本身的割裂。
首先可以看到这是一个内存数组,而且蓝色对应蓝色,黄色对应黄色等等。一个格子是1个字节。这个格子的地址是index。也就是真实地址模4的结果。
现在,时代变了,我们缓存大了一倍,此时呢,蓝色,黄色还想对应的话(还是4组的话),每一次的大小就要变了,现在还是最低的2位吗?不是了好像
本来我们看最后两位,但是现在变了。最后一位似乎没有发挥作用,真实的发挥作用的是倒倒数二三位!(他们是哪一行,是哪种颜色)而倒数第一位则表明他是蓝色中的哪一个(是哪一列,是颜色中的哪一个)
而我们把刚才的位丢弃,剩下的是cache number!他们表示有多少个组
但是我们喜欢看中间,就像吃馅饼把两边掰开似的。
对于一个块,offset是他的宽度,index是他的高度
可以看到offset是从右往左走的。
index承担了他的上下
孩子们我回来了,不过我tag变了。
这是否说明,其实cache的大小和我缓存的大小是一致的呢?
如果他就在教室,那我可以去教室,但是如果他不在,我必须去澳大利亚,把它带回来,顺便带一个回教室,这也太远了吧!
根据步长的不同,移动的距离也会有所不同。
以行,块,cache数为移动步长,移动方向分别为横向纵向和跨越。
步长自然也可以是块加上缓存数
代码的最开始都是cold,然后是通过一些访问,遍历进行warming up,然后
hot时大部分均可命中。
命中率和未命中率的计算方法。没有命中,你就要去澳大利亚拿东西,这就是你的惩罚。
哨兵位的作用
它是0,说明该缓存无效,否则数据有效
或许会有位,但大部分是垃圾,我们无法相信他们,他们就是小偷。给钱他们不会还
举个例子,14个位表示index+offset,4个位是offset。一个块是16kb,一行是16b,rag是18位。
由于举例的4个末位2位是0,所以他起码是字对齐的哦。
对该图进行一下解释,他的意思是,我们的内存本来offset应该是16个,4位。但是呢,因为我们的数据是一个字(4个内存地址)才有意义,所以这里我们又将内存地址中每2位作为了一个整体进行了拆分(原始的地址的1-4已经是索引了,这种索引本来就是用两位实现的)。然后一个offset是4位,拆分就结束了。
或者我们可以这么想,offset是4位的,而其中因为一个字才有意义,所以4位-2位,只有前边2位才是真正表示我们的数据的位。
一次加载我们会整整加载一个行,即16个bit的位中的数据。
哥们,现在你该明白了吧,其实缓存就这么大,只不过缓存可以支持我们把tag换出去,所以tag可以更换,使得多个tag可以并存了罢了。
IVTO
实际上他是可以把4个字的一行变成16个块的。
写回则是另一种操作