程序局部性原理: CPU 大多数时间在执行相同的指令或者与此相邻的指令

时间局部性VS空间局部性:

a. 时间局部性:当前访问的指令或数据,也可能在之后访问;

b. 空间局部性:当程序访问内存地址x时,可能很快会访问临近x的内存。

内存

重点需要关注的是,内存的速度还有逻辑上内存和系统的连接方式和结构。

控制内存刷新和内存读写的是内存控制器。

从逻辑上我们只需要把内存看成一个巨大的字节数组就可以,而内存地址就是这个数组的下标。

CPU 到内存的性能瓶颈

相比 CPU 的数据吞吐量,内存要慢上几个数量级。再加上多核心 CPU 同时访问内存,会导致总线争用问题,数据吞吐量会进一步下降。无论 CPU 的性能多高都没用,而内存才是决定系统整体性能的关键。

Cache

回到程序的局部性原理,用一块小而快的储存器,放在 CPU 和内存之间,就可以利用程序的局部性原理来缓解 CPU 和内存之间的性能瓶颈。这块小而快的储存器就是 Cache,即高速缓存。

Cache 中存放了内存中的一部分数据,CPU 在访问内存时要先访问 Cache,若 Cache 中有需要的数据就直接从 Cache 中取出,若没有则需要从内存中读取数据,并同时把这块数据放入 Cache 中。但是由于程序的局部性原理,在一段时间内,CPU 总是能从 Cache 中读取到自己想要的数据。

Cache 主要由高速的静态储存器、地址转换模块和 Cache 行替换模块组成。

Cache 会把自己的高速静态储存器和内存分成大小相同的行,一行大小通常为 32 字节或者 64 字节。

Cache 和内存交换数据的最小单位是一行,为方便管理,在 Cache 内部的高速储存器中,多个行又会形成一组。

除了正常的数据空间外,Cache 行中还有一些标志位,如脏位、回写位,访问位等,这些位会被 Cache 的替换模块所使用。

Cache 大致的逻辑工作流程如下:

  1. CPU 发出的地址由 Cache 的地址转换模块分成 3 段:组号,行号,行内偏移。

  2. Cache 会根据组号、行号查找高速静态储存器中对应的行。如果找到即命中,用行内偏移读取并返回数据给 CPU,否则就分配一个新行并访问内存,把内存中对应的数据加载到 Cache 行并返回给 CPU。

    写入操作则比较直接,分为回写和直通写,回写是写入对应的 Cache 行就结束了,直通写则是在写入 Cache 行的同时写入内存。

  3. 如果没有新行了,就要进入行替换逻辑,即找出一个 Cache 行写回内存,腾出空间,替换行有相关的算法,替换算法是为了让替换的代价最小化。

逻辑由Cache硬件独立实现,对软件是透明的。

Cache 带来的问题

Cache的副作用是一致性的问题。

以x86 cpu的cache结构图为例,Cache一致性问题,主要包括如下三个方面:

  1. 一个CPU核心中的指令cache和数据cache的一致性问题。(注: 第一级 Cache 是指令和数据分开的,目的是连续的指令在流水线工作时防止指令之间产生的资源冲突)

  2. 多个CPU核心各自的2级cache的一致性问题

  3. CPU的3级cache与设备内存,如DMA、网卡帧储存,显存之间的一致性问题。

对于程序代码运行而言,指令都是经过指令 Cache,而指令中涉及到的数据则会经过数据 Cache。指令是由操作码和操作数组成的,所以指令中也有数据。

典型的多核心 Cache 数据同步协议有 MESI 和 MOESI。MOESI 和 MESI 大同小异,下面我们就去研究一下 MESI 协议。

Cache 的 MESI 协议

MESI 协议定义了 4 种基本状态:M、E、S、I,即修改(Modified)、独占(Exclusive)、共享(Shared)和无效(Invalid)。

  1. M 修改(Modified):当前 Cache 的内容有效,数据已经被修改而且与内存中的数据不一致,数据只在当前 Cache 里存在。

  1. E 独占(Exclusive):当前 Cache 中的内容有效,数据与内存中的数据一致,数据只在当前 Cache 里存在;

  1. S 共享(Shared):当前 Cache 中的内容有效,Cache 中的数据与内存中的数据一致,数据在多个 CPU 核心中的 Cache 里面存在。

  1. 无效(Invalid):当前 Cache 无效。前面三幅图 Cache 中没有数据的那些,都属于这个情况。

Cache硬件会监控所有CPU上cache的操作,根据相应操作使Cache里的数据行在上面这些状态之间切换。Cache 硬件通过这些状态的变化,就能安全地控制各 Cache 间、各 Cache 与内存之间的数据一致性了。

开启 Cache

x86 CPU 上默认是关闭 Cache 的,需要在 CPU 初始化时将其开启。只需要将 CR0 寄存器中 CD、NW 位同时清 0 即可。CD=1 时表示 Cache 关闭,NW=1 时 CPU 不维护内存数据一致性。

mov eax, cr0 #开启 CACHE
btr eax,29 #CR0.NW=0
btr eax,30 #CR0.CD=0
mov cr0, eax

获取内存视图

物理地址空间中可能有空洞,有 ROM,有内存,有显存,有 I/O 寄存器,获取内存有多大没用,关键是要获取哪些物理地址空间是可以读写的内存。

x86 平台上,通过 BIOS 提供的实模式下中断服务来获取内存的地址空间。这个中断服务是 int 15h(https://wiki.osdev.org/Detecting_Memory_(x86)#BIOS_Function:INT_0x15.2C_EAX.3D_0xE820)。


_getmemmap:
xor ebx,ebx #ebx设为0
mov edi,E80MAP_ADR #edi设为存放输出结果的1MB内的物理内存地址 [ EDI称为目的变址寄存器,通常存放处理后的数据的内存地址 ]
loop:
mov eax,0e820h #eax必须为0e820h
mov ecx,20 #输出结果数据项的大小为20字节:8字节内存基地址,8字节内存长度,4字节内存类型
mov edx,0534d4150h #edx必须为0534d4150h
int 15h #执行中断
jc error #如果flags寄存器的C位置1,则表示出错
add edi,20 #更新下一次输出结果的地址
cmp ebx,0 #如ebx为0,则表示循环迭代结束
jne loop #还有结果项,继续迭代
ret
error: #出错处理

上面的代码是在迭代中执行中断,每次中断都输出一个 20 字节大小数据项,最后会形成一个该数据项(结构体)的数组,可以用 C 语言结构表示,如下。


#define RAM_USABLE 1 //可用内存
#define RAM_RESERV 2 //保留内存不可使用
#define RAM_ACPIREC 3 //ACPI表相关的
#define RAM_ACPINVS 4 //ACPI NVS空间
#define RAM_AREACON 5 //包含坏内存
typedef struct s_e820{
u64_t saddr; /* 内存开始地址 */
u64_t lsize; /* 内存大小 */
u32_t type; /* 内存类型 */
}e820map_t;

总结

思考题:

如何写出提高 Cache 命中率的代码?

参考答案:

1、遵从80-20法则,程序80%的时间在运行20%或更少的代码,针对热代码进行优化,才容易产出效果;

2、遵从数据访问的局部性法则,按数据存放顺序访问内存效率远高于乱序访问内存效率,也就是尽量帮助CPU做好数据Cache的预测工作。同样根据Cache大小,做好数据结构的优化工作,进行数据压缩或数据填充,也是提升Cache效率的好方式;

3、遵从指令访问的局部性法则,减少跳转指令,同样是尽量帮助CPU做好数据Cache的预测工作;现代CPU都有一些预测功能【如分支预测】,利用好CPU的这些功能,也会提升Cache命中率;

4、避免计算线程在多个核心之间漂移,避免缓存重复加载,可以绑定核心【物理核即可,不用到逻辑核】,提高效率;

5、去除伪共享缓存:在多核环境下,减少多个核心对同一区域内存的读写并发操作,减少内存失效的情况的发生;

6、合理提高进程优先级,减少进程间切换,可以变相提供Cache提速的效果

7、关闭Swap,可以变相提供内存提速、Cache提速的效果;

8、使用Intel自家的编译器,开启性能优化,很多时候可以提速运算效率;

9、使用C语言,而不是更高级的语言,很多时候可以提速运算效率;

10、直接使用昂贵的寄存器作为变量,可以变相提供加速效果;

参考

操作系统实战45讲

操作系统实战45讲笔记- 07 Cache与内存:程序放在哪儿?的更多相关文章

  1. MySQL实战45讲笔记一

    MySQL的基本架构大体可以分为server层和存储引擎层,逻辑架构图如下: Server层除了图中显示的,还包括所有的内置函数(包括日期.时间.数学和加密函数等),存储过程.触发器.视图等跨存储引擎 ...

  2. 极客时间 Mysql实战45讲 07讲行锁功过:怎么减少行锁对性能的影响笔记 极客时间

    极客时间 Mysql实战45讲 07讲行锁功过:怎么减少行锁对性能的影响笔记 极客时间极客时间 Mysql实战45讲 07讲行锁功过:怎么减少行锁对性能的影响笔记 极客时间 笔记体会: 方案一,事务相 ...

  3. Mysql实战45讲 06讲全局锁和表锁:给表加个字段怎么有这么多阻碍 极客时间 读书笔记

    Mysql实战45讲 极客时间 读书笔记 Mysql实战45讲 极客时间 读书笔记 笔记体会: 根据加锁范围:MySQL里面的锁可以分为:全局锁.表级锁.行级锁 一.全局锁:对整个数据库实例加锁.My ...

  4. Mysql实战45讲 05讲深入浅出索引(下)极客时间 读书笔记

    极客时间 Mysql实战45讲 04讲深入浅出索引(下)极客时间 笔记体会: 回表:回到主键索引树搜索的过程,称为回表覆盖索引:某索引已经覆盖了查询需求,称为覆盖索引,例如:select ID fro ...

  5. Mysql实战45讲 04讲深入浅出索引(上)读书笔记 极客时间

    极客时间 Mysql实战45讲 04讲深入浅出索引 极客时间(上)读书笔记  笔记体悟 1.索引的作用:提高数据查询效率2.常见索引模型:哈希表.有序数组.搜索树3.哈希表:键 - 值(key - v ...

  6. 深挖计算机基础:MySQL实战45讲学习笔记

    参考极客时间专栏<MySQL实战45讲>学习笔记 一.基础篇(8讲) MySQL实战45讲学习笔记:第一讲 MySQL实战45讲学习笔记:第二讲 MySQL实战45讲学习笔记:第三讲 My ...

  7. MySQL实战45讲学习笔记:第三十九讲

    一.本节概况 MySQL实战45讲学习笔记:自增主键为什么不是连续的?(第39讲) 在第 4 篇文章中,我们提到过自增主键,由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧 ...

  8. 《MySQL实战45讲》(1-7)笔记

    <MySQL实战45讲>笔记 目录 <MySQL实战45讲>笔记 第一节: 基础架构:一条SQL查询语句是如何执行的? 连接器 查询缓存 分析器 优化器 执行器 第二节:日志系 ...

  9. 《MySQL实战45讲》个人笔记-基础篇

    拜读了林晓斌大佬的<MySQL实战45讲>,特意做个知识点总结,以便后期回忆. 01.基础架构:一条SQL查询语句是如何执行的? Server 层包括连接器.查询缓存.分析器.优化器.执行 ...

  10. 《MySQL实战45讲》学习笔记1——MySQL的基础架构

    在<极客时间>订阅了<MySQL实战45讲>专栏,总觉得看完和没看一样

随机推荐

  1. 【KAWAKO】从mac上定时将腾讯云的数据备份到本地

    目录 前言 需求 宝塔面板 备份网站 备份数据库 mac端 创建工程文件夹 rua.py rua stdout plist Reference 前言 不信任一切云端平台,把数据牢牢握在自己手中才是最安 ...

  2. JZOJ 3213. 【SDOI2013】直径

    题目 思路 树的直径很好求,两遍 \(dfs\),记下两个端点 然后很显然所有直径经过的边必然在我们求出的这条直线上 那么我们只要判断一下一条直径上的边是不是答案 假设当前边为 \(i\) 那么把 \ ...

  3. 网页实时显示已经运行了多少天 html+js

    以下为html代码: 放在body标签里面 <a>本站已经运行<a id="days">0</a>天</a> 以下为js代码: 放在 ...

  4. win11 文件夹内关闭显示更多

    使用命令提示符 打开cmd窗口,输入  reg add HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\Inpro ...

  5. Java第二讲动手动脑

    1. 运行结果 上述代码方法名相同,但是数据类型不同,体现了Java重载的特点(1)方法名相同(2)参数类型不同,参数个数不同,或者是参数类型的顺序不同.也体现了Java中,当函数名相同时,会通过 参 ...

  6. InputManager

    inputManger配置参数说明 https://www.cnblogs.com/xiaoyulong/p/10011256.html 配置方法 https://www.cnblogs.com/td ...

  7. flexible.js源码分析

    (function flexible(window,document){ // 获取html的根元素 var docEl = document.documentElement; // dpr 物理像素 ...

  8. UVM——通过一个简单的testbench来了解UVM组件的phase执行顺序

    先写好一个top.sv 查看代码 // 导入VCS或者Modelsim自带的UVM库和宏 `include "uvm_macros.svh" import uvm_pkg::*; ...

  9. RIDE,如何指定report,log,output的存放位置

    创建测试用例,执行后,report.html,log.html,output.txt 会默认存放到 C:\Users\你的用户名\AppData\Local\Temp下, 例如我的存放在 如果要指定存 ...

  10. kail 系统更新

    原文链接:https://blog.csdn.net/aiming66/article/details/123203495