计算机系统基础笔记(9)——高速缓存

前言

这章也是重点
话不多说 上知识^^

一、高速缓存的结构和工作原理

(一)几个缓存设计原则

  • 每一个缓存项都要包含“标签” (ID)和“内容”
  • 通过对有效位置建立索引和约束规则判断给定的数据是否被缓存
  • 未命中时,通常要选择一个现有缓存项,并被新缓存项所替代,称之为“替换策略”
  • 在写入时,需要传播更改的内容,或将该缓存项标记为“脏”( 直写与回写)
    这些都会在之后提及

(二)CPU高速缓存

以CPU中的高速缓存为例,这个也是重点 要知道三种不同的高速缓存类型

1.CPU高速缓存

  • CPU高速缓存是一个小型的,快速的,基于SRAM技术的存储器,由硬件自动控制(对用户透明),用于保存频繁访问的主存数据块
  • CPU首先在缓存(例如,L1、L2和L3)中查找数据,然后才会在主存中查找数据(就是按照已有的存储器结构)
    如图
    在这里插入图片描述

2.通用的高速缓存组织结构(S E B )(重点)

  • 缓存容量C = S x E x B bytes
    S为组
    E为行
    组包含行 行包含块
    B为每块的字节数
    每一块里面有有效位以及标记位(tag) 以及数据
    在这里插入图片描述

3.读高速缓存

首先我们拿到一段字地址
我们将其分为三段(标记 组索引 以及块内偏移量)
在这里插入图片描述

(1)确定组的位置

我们通过组索引找到对应的组
在这里插入图片描述

(2)检查是否有组中的行能够匹配标记

匹配 且 行的有效位有效 : 命中

1)直接映射高速缓存

每组里面只有一行

  • 因此直接与该行匹配,同时进行有效位和标记位的匹配,如果命中则可以继续
  • 如果标记未匹配:原有的行会被回收并替换
    在这里插入图片描述

下面是一个例子
给定一个字地址
在这里插入图片描述
我们假设

  • 4-bit addresses
    M=16 bytes
    B=2 bytes/block,
    S=4 sets
    E=1 Blocks/set
    我们跟踪一下每次输入字地址时的命中情况
    在这里插入图片描述
    地址0 ,7,8是冷未命中
    地址1命中是因为地址0未命中后加载到了高速缓存中
    第二个地址0是因为地址8未命中后加载到了组0中,产生了冲突未命中(中间两位是组索引)
    下面是高速缓存的情况
    在这里插入图片描述
2)E路组相联高速缓存

假设E = 2(行数)

  • 同时对两组进行比较
    -如果没有成功匹配
    • 选择当前组中的一行被回收和替换
    • 替换策略:随机,最近最少使用
      在这里插入图片描述

下面是例子
给定一个字地址(组索引为1 因为只有两组)
在这里插入图片描述
假设:

  • 4-bit addresses
    M=16 bytes(块大小)
    B=2 bytes/block,
    S=2 sets
    E=2 Blocks/set
    我们跟踪一下每次输入字地址时的命中情况
    在这里插入图片描述
    同样的 可以得到
    0,7是冷未命中
    1和第二个0命中
    8冲突未命中

下面是高速缓存的情况
在这里插入图片描述

3)全相联高速缓存(只有一个组)
  • 全相联映射允许每一个内存块装入高速缓存中的任意行,称为全相联高速缓存。全相联高速缓存只包含一个缓存组,并由所有高速缓存行组成
    在这里插入图片描述

原文在这里

(3)根据偏移量定位数据在行中的起始位置

如图
直接映射高速缓存
在这里插入图片描述E路组相联高速缓存
在这里插入图片描述

(三)如何处理写操作

1.处理写命中的情况

  • 直写:立即写入主存
  • 回写:至行被替换才写入主存
    (直写确保了数据的保存及时 回写提高了效率)

2.处理未命中的情况

  • 写分配 (加载至缓存,然后更新缓存中的行)
  • 非写分配 (直接写入内存,不加载数据块至缓存)
    (写分配提高了效率 非写分配确保了数据的保存)

因此有下表总结
在这里插入图片描述

(四)Intel 酷睿 i7处理器高速缓存层次结构

如图
在这里插入图片描述

(五)高速缓存的性能指标(重点)

1.未命中率

  • 在缓存中找不到所引用的内存数据的百分比(未命中次数/总访问次数
  • 未命中率 = 1 – 命中率
  • 一级缓存:3% ~ 10%
    二级缓存中这个值非常小(例如:<1%),取决于缓存
    容量等因素

2.命中时间

  • 将缓存中的行数据发送到处理器所需的时间
    • 包括判定该行是否在缓存中的时间
  • 一级缓存:1-2个周期
    二级缓存:5-20个周期

3.未命中惩罚

  • 由于缓存未命中所需要的额外数据访问时间
  • 主存:50-200个时钟周期
    • 趋势还在扩大!

4.为什么使用未命中率

  • 考虑下面的情况:命中时间1个周期,未命中惩罚100个周期
    • 平均访问时间 = 未命中率*未命中惩罚 + 命中率 * 命中时间
    • 97% hits: 1 cycle + 0.03 * 100 cycles = 4 cycles
      99% hits: 1 cycle + 0.01 * 100 cycles = 2 cycles
      所以未命中率更能直观地看到倍数上的差距

(六)编写缓存友好的代码

  • 减少内部循环中的缓存未命中
    • 对变量更好的重复利用(时间局部性)
    • 尽量使用步长为1的模式访问存储器(空间局部性)
  • 核心思想:通过理解高速缓存的工作机制,量化我们对局部性原理的定性认知

二、高速缓存对软件性能的影响

(一)通过循环重排提高空间局部性特征

1.矩阵的未命中率分析

假设有:

  • 每个矩阵是一个n×n的数组,数据类型为double,sizeof(double)==8
  • 缓存块大小为32字节(可以容纳4个64位数据)
  • 矩阵的维度(n)是一个非常大的值:1/N趋近于0
  • 缓存的容量较小,不足以同时缓存矩阵中的多行数据
    如下在这里插入图片描述
(1) 矩阵乘法
  • n x n 矩阵相乘
  • 共O(N3) 次操作(时间复杂度)
  • 每个元素都需要进行N次读操作
  • 每个目标元素都需要进行N次累加
    • 这些累加可能会在寄存器中进行

代码如图
在这里插入图片描述

1)C语言数组在内存中的布局
  • C语言数组以“行优先”方式分配内存
    每一行的元素在内存中的位置都是连续的

  • 在一行中,逐列进行访问

    • 访问连续的元素
    • 如果块大小B大于8字节,则可利用空间局部性
    • 未命中率 = 8/B
      在这里插入图片描述
  • 在一列中,逐行进行访问

    • 依次访问元素之间距离很远
    • 没有空间局部性!
      未命中率 = 1 (即100%)
      在这里插入图片描述
      我们试着按照以上原则改变遍历的次序 并进行未命中率分析 有
2)矩阵乘法(ikj)

在这里插入图片描述

3)矩阵乘法(jki)

在这里插入图片描述

4)矩阵乘法(kji)

在这里插入图片描述

5)矩阵乘法总结
  • ijk (& jik):
    ◼ 2次内存加载,0次存储
    ◼ 未命中次数/循环 = 1.25
  • kij (& ikj):
    ◼ 2次内存加载,1次存储
    ◼ 未命中次数/循环 = 0.5
  • jki (& kji):
    ◼ 2次内存加载,1次存储
    ◼ 未命中次数/循环 = 2.0
    在这里插入图片描述

(二)通过分块提高时间局部性特征

1.矩阵乘法

假设:
◼矩阵中的元素是double类型
◼缓存块容量是8个double类型数据(64字节)
◼缓存总的容量 C << n (远远小于n)

  • 第一次迭代
    • n/8 + n = 9n/8 misses (未命中)

在这里插入图片描述

  • 随后在缓存中
    在这里插入图片描述

  • 第二次迭代

    • n/8 + n = 9n/8 misses (未命中)在这里插入图片描述
  • 总的未命中次数:9n/8 * n2 = (9/8) * n3

2.分块矩阵乘法

代码如下
在这里插入图片描述
这是它的形象遍历
在这里插入图片描述

  • 假设:
    ◼矩阵中的元素是double类型
    ◼缓存块容量是8个double类型数据(64字节)
    ◼缓存能够容纳三个“块”:3B^2<C

  • 第一次(块)迭代

    • 每个块出现B2/8次未命中
    • 有 2n/B * B2/8 = nB/4 次未命中(忽略矩阵C)
      在这里插入图片描述
      在这里插入图片描述
  • 第二次(块)迭代

    • 有 2n/B * B2/8 = nB/4 次未命中
      在这里插入图片描述
  • 总未命中次数

    • nB/4 * (n/B)2 = n3/(4B)

(三)存储器山

1.存储器山

  • 读吞吐量(读带宽)
    ◼从存储系统中读取数据的速率 (MB/S)
  • 存储器山:一个读吞吐量关于时间和空间局部性的函数关系
    ◼一种表征存储系统性能的简洁方法
    在这里插入图片描述

2.测量存储器山的函数

  • 使用不同的elems和stride的参数组合,调用
    test()函数
    ◼对于每一个 elems 和 stride 参数
    ◼首先,调用一次test(),对缓存进行“预热”(因为第一次调用一定是冷未命中)
    ◼然后,再次调用test(),然后测量读吞吐量
    在这里插入图片描述

总结

存储器山了解概念即可
高速缓存的结构这部分 要知道三种不同的高速缓存类型 以及 高速缓存的性能指标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值