深入浅出计算机组成原理
徐文浩
bothub 创始人
70432 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 62 讲
深入浅出计算机组成原理
15
15
1.0x
00:00/00:00
登录|注册

37 | 高速缓存(上):“4毫秒”究竟值多少钱?

二维数组的访问性能比较
组相连Cache的学习资源
现代CPU的Cache设计
CPU Cache的设计目的
程序性能瓶颈来自DRAM芯片的内存访问速度
CPU Cache带来的商业利益
高频交易公司利用微秒级别的时间优势
组相连Cache
直接映射Cache
Cache的数据结构
CPU访问Cache的流程
CPU Cache的作用
CPU和内存的性能差距
课后思考
推荐阅读
总结延伸
减少4毫秒,公司挣了多少钱?
Cache的数据结构和读取过程是什么样的?
为什么需要高速缓存?
CPU Cache

该思维导图由 AI 生成,仅供参考

在这一节内容开始之前,我们先来看一个 3 行的小程序。你可以猜一猜,这个程序里的循环 1 和循环 2,运行所花费的时间会差多少?你可以先思考几分钟,然后再看我下面的解释。
int[] arr = new int[64 * 1024 * 1024];
// 循环1
for (int i = 0; i < arr.length; i++) arr[i] *= 3;
// 循环2
for (int i = 0; i < arr.length; i += 16) arr[i] *= 3
在这段 Java 程序中,我们首先构造了一个 64×1024×1024 大小的整型数组。在循环 1 里,我们遍历整个数组,将数组中每一项的值变成了原来的 3 倍;在循环 2 里,我们每隔 16 个索引访问一个数组元素,将这一项的值变成了原来的 3 倍。
按道理来说,循环 2 只访问循环 1 中 1/16 的数组元素,只进行了循环 1 中 1/16 的乘法计算,那循环 2 花费的时间应该是循环 1 的 1/16 左右。但是实际上,循环 1 在我的电脑上运行需要 50 毫秒,循环 2 只需要 46 毫秒。这两个循环花费时间之差在 15% 之内。
为什么会有这 15% 的差异呢?这和我们今天要讲的 CPU Cache 有关。之前我们看到了内存和硬盘之间存在的巨大性能差异。在 CPU 眼里,内存也慢得不行。于是,聪明的工程师们就在 CPU 里面嵌入了 CPU Cache(高速缓存),来解决这一问题。

我们为什么需要高速缓存?

按照摩尔定律,CPU 的访问速度每 18 个月便会翻一番,相当于每年增长 60%。内存的访问速度虽然也在不断增长,却远没有这么快,每年只增长 7% 左右。而这两个增长速度的差异,使得 CPU 性能和内存访问性能的差距不断拉大。到今天来看,一次内存的访问,大约需要 120 个 CPU Cycle,这也意味着,在今天,CPU 和内存的访问速度已经有了 120 倍的差距。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

现代CPU中的Cache对于性能提升至关重要。CPU通过将内存中的数据加载到Cache中,实现更快的数据访问,弥补了内存访问速度与CPU性能提升之间的差异。Cache采用直接映射、组相连等策略,通过索引、组标记和偏移量的方式将大内存地址映射到小的CPU Cache地址中。这种设计使得CPU能够快速定位内存数据,大大减少了等待内存访问的时间。文章还通过实例说明了CPU Cache的重要性,以及微秒级别的性能差异对商业利益的巨大影响。此外,文章还提到了高频交易行业利用微秒级别的时间优势进行套利的案例。总之,现代CPU通过Cache的设计,弥补了CPU和内存性能差异,为提升计算机性能发挥了重要作用。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入浅出计算机组成原理》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(44)

  • 最新
  • 精选
  • 程序员花卷
    我的电脑测试的结果 int[][] arr = new int[10000][10000]; 按行迭代—— 280毫秒 按列迭代—— 1180毫秒 分析原因—— 1、首选数组的存储方式是连续的,在内存中是按照行来存储的,遍历的时候也是一个一个的往后遍历 2、根据老师讲的,CPU从内存读取数据到CPU Cache ,是按照一小块一小块的方式读取的,它的物理内存是连续的,几乎是同行不同列,如果说我们是按照列来迭代的话,那么就会导致存储快无法使用,我们就不得不从内存中读取数据,而在内存中直接读取数据和从高速缓存中直接读取数据的效率那可不是一般的差距,所以说按照行来迭代话,我们就可以很好的利用的数据块,从高速缓存中来读取数据,从而所花费的时间也就比按照列来迭代所花费的时间少! 这是我的见解,有不对的地方,还望老师指正!

    作者回复: Hash同学, 你好,理解正确!

    2019-12-23
    4
    62
  • 前端西瓜哥
    有点像哈希表。那如果读取的多个内存数据的地址都指向同一个缓存块怎么办?直接覆盖掉?

    作者回复: Fstar同学你好, 37和38讲里讲了怎么做组相连的缓存加载过程,后面的MESI协议部分里也有怎么做写入的高速缓存同步问题,可以仔细看一下。

    2019-09-27
    7
  • 小先生
    我有个疑问: 内存中读取数据,也是按照一块一块来的。 那一个内存地址是怎么存储对应字的位置偏移量。那它得存多少偏移量啊?

    作者回复: 小先生同学, 你好,偏移量是在内存地址的访问请求里的,并不会存在映射关系里,不需要预先存啊。

    2019-09-19
    3
    7
  • 斐波那契
    一般二维数组在内存中存放是按行优先存放的 所以在加载数据时候就会把一行数据加载进cache里 这样cache的命中率就大大提高 如果按列迭代 cache就很难名字从而cpu就要经常从内存中读数据 如果数据量不大的话两种方式可能没什么感觉 一旦数据量很大的话按行迭代的速度就能感觉出来了
    2019-07-19
    1
    30
  • 尼古拉斯
    8个缓存块 应该是cake line 0 到cake line 7 途中多了一个cake line 8
    2020-10-27
    4
  • 李二木
    我们为什么需要高速缓存? CPU 的速度好比风驰电掣的高铁,而内存像脚不太灵便的老太太,速度不匹配 Cache 的数据结构和读取过程是什么样的 现代 CPU 进行数据读取的时候,无论数据是否已经存储在 Cache 中,CPU 始终会首先访问 Cache。只有当 CPU 在 Cache 中找不到数据的时候,才会去访问内存,并将读取到的数据写入 Cache 之中 缓存放置策略 1) 通过直接映射Cache, CPU访问内存数据,是一小块一小块数据来读取的。对于读取内存中的数据,我们首先拿到的是数据所在的内存块(Block)的地址。而直接映射Cache采用的策略,就是确保任何一个内存块的地址,始终映射到一个固定的CPU Cache地址(Cache Line)。而这个映射关系,通常用 mod 运算(求余运算)来实现。 比如说,我们的主内存被分成 0~31 号这样 32 个块。我们一共有 8 个缓存块。用户想要访问第 21 号内存块。如果 21 号内存块内容在缓存块中的话,它一定在 5 号缓存块(21 mod 8 = 5)中 一个内存的访问地址,最终包括高位代表的组标记、低位代表的索引,以及在对应的Data Block中定位对应字的位置偏移量 2) 全相连 Cache(Fully Associative Cache)、 一个快可以放置在cache中的任何位置,但是在检索cache中所有项,为了使检索更加有效,它是由一个与cache中每个项都相关的比较器并行完成,这些比较器加大了硬件开下,因而全相连只适合块较少的cache 3) 组相连 Cache(Set Associative Cache) 介于直接映射和全相连之间的设计是组相连,在组相连cache中,每个块可被放置的位置数是固定的。每个块有n个位置可放的cache被称作为n路组相连cache。一个n路组相联cache由很多个组构成,每个组中有n块,根据索引域,存储器中的每个块对应到cache中唯一组,并且可以放在这个组中的任何一个位置上。
    2021-03-17
    3
  • Monday
    细细品来,真有味
    2020-06-13
    3
  • coldpark
    请问如果5号高速缓存块要同时存储5和21号内存的数据,组标记怎么填写呢?
    2019-10-03
    1
    3
  • Geek_JaneJane
    请问徐老师,为什么我在Python上执行3行小程序不是那个效果呢? ``` import time a = [0]*64*1024*1024 start = int(time.time()*1000) for i in range(0, len(a)): a[i] *= 3 print '1,', int(time.time()*1000) - start start = int(time.time()*1000) for j in range(0, len(a), 16): a[j] *= 3 print '2,', int(time.time()*1000) - start 输出: 1, 11804 2, 538 ```
    2019-07-20
    4
    3
  • Magic
    按行迭代刚好匹配空间局部性原理,因此性能更好
    2020-10-09
    2
收起评论
显示
设置
留言
44
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部