【数据结构】比较顺序表和链表的区别(优缺点),细讲CPU高速缓存命中率

目录

一、顺序表和链表的区别【表格】

二、顺序表优缺点

三、链表优缺点

四、缓存命中率(缓存利用率)

❥ 主存和本地二级存储

❥ 寄存器和三级缓存

❥ 顺序表缓存命中率

❥ 链表缓存命中率


一、顺序表和链表的区别【表格】

不同点顺序表链表(带头双向循环链表)
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问(用下标随机访问)支持   复杂度为O(1)不支持   复杂度为O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低  复杂度为O(N)只需要修改指针的指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问频繁插入和删除任意位置
缓存利用率

二、顺序表优缺点

优势

  • 能够通过下标进行随机访问
  • 物理上一定连续
  • 缓存利用率高

缺点

  • 扩容有消耗。扩容分为原地扩容和异地扩容,当为异地扩容时,消耗容量更多,因为还要进行数据的拷贝到新的空间,然后再释放掉原来的空间。
  • 扩容还有可能存在空间浪费。例如:开辟了100个空间,却只用了80个,浪费了20个空间。
  • 在插入删除时需要移动大量的元素

三、链表优缺点

优势

  • 可以任意位置插入和删除元素
  • 没有扩容的消耗,可以按需申请和释放
  • 不浪费空间

缺点

  • 不能用下标随机访问。所以链表排序意义不大,不如用数组进行排序(因为不能随机访问)
  • 缓存利用率低

所以说,顺序表和链表是互补的线性结构

四、缓存命中率(缓存利用率)


❥ 主存和本地二级存储

下图1是计算机存储器的层次结构。计算机的内部存储体系分为很多层,其中有两个使我们所熟知的,分别是

  • 主存:也就是电脑的内存
  • 本地磁盘:也就是硬盘

内存和硬盘的区别:

  • 内存:带电存储,速度很快,一个电脑内存一般是8/16G。
  • 硬盘:不带电存储,速度较慢。

虽然他们的区别是速度快慢问题,但本质区别是他们是否带电存储。

那何为带电不带电存储呢?

举个例子 ! ! !

        假设我们正在word上写笔记,这时候如果我们把电脑里的电池扣掉,那么正在word上写的笔记就会消失,再打开电脑就需要我们重新再写,为什么呢?

        这是因为我们写的笔记不是存在硬盘中,而是存在内存。

        为什么不存在硬盘呢?

        是因为硬盘的速度比较慢。

        那什么情况下它才会存在硬盘中呢?

        我们点击左上角的保存(或者直接ctrl+s),鼠标转两个圈圈(转圈说明速度较慢),这样就会保存到文件中,也就保存到了硬盘当中。

        若是存好之后我们又想修改一下我们的笔记,那修改之后的内容是不会存到文件中的,需要我们再次进行保存。

所以金字塔越往下存储容量越大,访问速度越来越慢,成本价格也越来越低。


❥ 寄存器和三级缓存

除此之外,我们发现每个电脑都还有一个寄存器和三级缓存。

补充:CPU和寄存器定义

  • CPU(中央处理器)是一块超大规模的集成电路,它是计算机的运算核心和控制核心。它由控制单元、运算单元和存储单元组成,负责解释计算机指令和处理软件中的数据。
  •  寄存器是位于CPU内部的一种物理存储器,用于高速存储指令和数据,设计目的是在CPU内部快速访问这些存储区域。

我们已经有了内存,且内存速度也还行,为什么还要有三级缓存呢?

分析如下:

代码:

int a = 0;
++a;

转到反汇编(汇编就是对应的指令),如下图所示:

我们定义了变量a,这个变量i是存储在内存中,现在对a执行下一步指令(即++a)。

那么谁来对i执行++a指令呢?

是CPU。CPU可以执行各种各样的指令(加减乘除,异或等)。

上图我们可知:++a转到反汇编,分为三步执行:

  • 首先它不是直接对内存a的位置进行++,它是把a从内存中读取出来,存储到eax这个寄存器
  • 然后执行inc eax指令,对寄存器中的值进行自增
  • 最后再把寄存器中自增完的值返回到ptr[a]这个位置。

为什么要这样做呢?

是因为它们不同频,cpu运行的速度太快,如果它直接去访问内存,把内存数据访问到,然后对内存的数据直接进行++,速度太慢;所以更愿意把这个数据从内存加载到寄存器,(因为访问寄存器很快,越往上速度越快),然后在寄存器中cpu执行这个指令(cpu中有加法器,执行完之后再把这个结果返回到内存里面),而不是去访问内存。

cpu通常是不会直接去访问内存,如果去访问内存的数据,内存的数据比较小,通常就会加载到寄存器。

这里的小指的是4/8个字节。

如果比较大就会加载到缓存(三级缓存),一级一级加载。


假设我们要执行一段指令,要访问顺序表和链表里的一段数据,例如:1 2 3 4 

在这里这些数据分别是怎么访问的呢?

我们知道,不管是顺序表还是链表,这些数据都是在内存中(堆上)的。

假设我们要执行顺序表或链表 +1 -1 等类似操作,cpu是不会直接去访问这个内存的,而是加载到缓存去访问。

加载到缓存的原因:

因为这些数据1 2 3 4 太大,所以它们不会加载到寄存器中,而是加载到缓存当中。

假设我们要访问1 2 3 4 这一段数据,cpu就会查看首元素的地址在不在缓存

  • 如果在缓存,就叫缓存命中,可以直接去访问
  • 如果不在缓存,就叫不命中,数据会先从内存加载到缓存,再访问。

那到底是怎么加载到缓存呢?

在实际加载过程中,cpu每次不会只加载一个字或一个字节,而是一块块地加载(原因:局部性原理),它会认为当前位置的数据被访问了,那与之相邻地址的数据有很大概率也会被访问

打个比方:

每个班里应该都发生过这种情况,我们正在上课。

这时候班主任突然进来说:5号你们这一列出来去把新书搬到班里。

到了楼下,班主任发现运了一车书,心想:这3个学生去搬不得累坏了。于是又到班里点了一列学生:4号这一列也出来一下。

我们发现:当班主任把5号这列叫走的时候,再去叫的时候最大可能就是叫4号这列,也就是和他们相邻的那一列。如果班主任叫人的时候,东叫一个,西叫一个,那班里不得乱糟糟的,任课老师怎么讲课啊!


那到底是加载多少字节到缓存呢?

一般是几十个字节,甚至是多少k,缓存会按照它的定义去拉取多少,它跟cpu的型号有关,叫作CPU的字长

其实读这些数据就是探测这些二进制位是0还是1,把这个01通过电信号位读到了以后然后拿过来,它是有很多很多的线连接着,cpu访问一次,有多少根线,就是访问了多少个比特位。


❥ 顺序表缓存命中率

假设最开始去访问这些数据,这些数据是都没有被访问过的,也就在缓存中,那第一个数据就是不命中。

如果是数组的话,由于第一个数据不命中,会把首地址开始的一长段数据都加载到缓存中去,如果再访问第2个位置的数据,则在缓存,也就是缓存命中。

如果数据特别长,第一个不命中,然后后面连续一长段都命中,再不命中,然后再加载到缓存,后面一长段命中......以此类推。

这就叫做:缓存利用率高(缓存命中率高)。

❥ 链表缓存命中率

那链表为什么缓存利用率低呢?

我们知道,数组在内存中是连续存放的,而链表节点与节点之间的内存不一定连续,因为每次malloc的地址都是随机的。

第一次访问的时候,我们把第一个数据后面一连串连续的地址加载到缓存,虽然有可能会访问到后面的节点,但通常情况下是访问不到的,最坏的情况是后面的一个节点都没访问到,这就会造成:缓存利用率低(缓存命中率低)。

除了不命中之外,还会导致缓存污染。

缓存污染意思是:缓存空间大小是一定的,加载进来却没有用的数据把其它数据挤走,这就造成了缓存污染。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值