本文是个意外,就像有时候忘了那啥一样,纯属意外,大家看个热闹,就当个预热吧,打算写这部分的,内容太多了,看资料看的头疼
本文源于一个 JY 对于 volatile 可见性的疑问,下面是我的作答:
很高兴又看到一个愿意刨根问底的小伙伴,其实你的问题非常好,核心本质是你不了解 CPU 的整套运行逻辑。整个 CPU 世界都跑在这套底层运行逻辑上的,所有的上层逻辑都要依附于,适配这套底层运行逻辑 CPU 硬件包括 2 方面: 硬件 IC 设计 + CPU运行逻辑(CPU 驱动),CPU 也是有驱动的
MESI 协议是在多线程技术之后产生的,很古老,之后又诞生了 多核、乱序执行、流水线 等新的技术。CPU 这个领域毕竟是一个商业领域,技术的选择要以商业表现来衡量,在技术的发展中一定会遇到相互矛盾的时刻,MESI 协议就是这样。 MESI 协议是顺序一致性的,在处理消息时必然造成整个流水线停顿等待数据刷新,而这个等待成本非常的大,甚至说是致命的
现代 CPU 一个时钟周期可以处理 2-3 条指令,L1 的访问速度 1-2 个时钟周期,L2/L3 的访问速度会依次更慢,到内存时访问速度会达到 80-150 个时钟周期,小伙伴你想想,MESI 停顿的代价肯定是不能接受的,你停顿一下的时机都够完成整个方法的计算了...所以现在都有一个 InvalidataQueue 无效队列,所有的 MESI 发送的无效消息接收都是直接接受的,但是执行的话就是立即了,而是等待 cpu 空闲或者与内存、缓存的仿存操作结束后再执行
我把现代 CPU 流水线大概给你说一下:(以 ARM A53 为例)
- 取指: 4 个时钟周期去取一次指令,一次取 4-6 条,然后储存在 CPU 核心制定缓存内
- 解码: 里面有 2-4 个独立队列,每个队列在 4 个时钟周期内把自己分配到的指令 解码成 更微小的,CPU 硬件 IC 能处理的微指令,然后这个微指令解码后被发送到 CPU 内部制定缓存队列中
- 乱序发射: 没有逻辑相关性的微指令都会被排入发射队列的待发射队列中,一旦下游处理单元空闲就发过去一条微指令去真正的计算 例子:int a = 0; int a++; int b = a+2; int c = 3+1; b 在逻辑上依赖 a,c 不依赖别人,所以 a、c 的计算都会进入待发射队列等待执行,b 只有等待前面的 a 计算完成才会进入待发射队列
- 执行单元: 这里处理每一个伪指令,a++ 就是在这里真正计算的,每个 CPU 都有至少 10 几个 执行单元,相同类型的执行单元都是复数,都有多个,并行执行。执行单元需要的微指令由上一步乱序执行待发射队列传送过来
- 重命名队列: 我们代码是按顺序写的,那么最终怎么保证结果也是顺序正确的,这一步就是做的这个事。我们写的一条代码最终解析成多条微指令在 CPU 中执行,那么上一步执行单元计算完的结果会先保留在 一个专门的队列中,当着一条代码解析出来的所有微指令全部执行完成,再综合出一个结果,然后这一条指令才算完成,相当于数据库中的事务。解码出的微指令是一条一条进入不同的处理单元的,但是刷新计算结果时是打包还原成一条完整的指令写回内存、缓存的
上面我对 CPU 流水线的描述并不是非常正式,是我随笔简单写的,目的便于大家有个初步理解
CPU 流水线大概基本就是这样,我们回到 volatile 的问题,cpu 指令集针对要在 乱序执行中 保持 顺序执行的特性,提供了 内存屏障,有多条不同类型,成组的内存屏障指令,高于语言的编译器在 看见 volatile 修饰的数据时,会根据自身语言的特性和 CPU 平台,选择不同的内存屏障组指令 在 内存屏障指令之后的 r/w/rw 操作都必须等内存屏障指令前面的执行完成才能进入待发射队列,用内存屏障指令给 volatile 之后的操作人为加上了一个逻辑相关性
然后你写一个 volatile 就能实现可见性了。为啥,volatile 之后的操作即便在 cpu 内部解码完成了,也得在 乱序执行单元的缓存中等待 volatile 加的内存屏障指令之前的代码都执行完,这里说的代码可是逻辑上的代码。你想 整个 CPU 都停下来等了,MESI 协议自然有充足的时间去完成自己的任务。完成了 volatile 语句在 重命名队列 中完成弹出队列了,后面的操作才能执行 这个代价就是整个 CPU 停顿,等... 时间成本相当高,对 CPU 效率响应巨大,能直接把 cpu 性能瞬间拉低到 1% 的程度
所以有句话,volatile 能不用就不用,一个是因为容易出问题,一个是性能太低了
cpu 运行逻辑要是有兴趣可以联系我要书单: 这块的书都很贵的,最贵的一本 180 (;¬_¬)
书单
2024.2.8 日新增
有 JY 给我留言,想看道完整的书单,这里我发一下,我自己的进度的确比预期慢了很多,因为体系化学习,我会不听调整学习顺序、优先级
计算机体系结构又称:CPU 硬件体系结构,从硬件层的 CPU 到软件层的 OS、再到语言层的 C/C++、编译器,都是相互串联在一起发挥功效的,甚至就连我们通常认为的硬件 CPU 也是有驱动的,单单一个硬件真的就能让 OS 在上面跑了吗?
NO!!! CPU 在设计上和 OpenGL 的思路一样,相关人员坐在一起,设计规范出一系列统一的逻辑、思想,最后转化为接口、地址等,其中部分逻辑由 OS 层实现,CPU 去读取。部分由 CPU 实现,放在指定 Bios、芯片内
注:
- 请大家首先有熟练的 C/C++ 语言功底
- 本书单不包含 EDA 设计,只介绍体系知识,意在让大家了解
书单 ->
- 必读第一本:
- 《CPU 通识课》
- 圣经 3 本:
- 《计算机体系结构:量化研究方法》①
- 《计算机组成与设计:硬件/软件接口》②
- 《深入理解计算机系统》③
- 必备扩充:
- 《计算机体系结构基础》
- 《CPU 设计实战》
- 《ARM64 体系结构编程与实践》
- 《RISC-V 体系结构编程与实战》
- 其他:
- 《计算机底层的秘密》
- 《CPU 眼里的 C/C++》
- 《趣话计算机底层技术》
阅读提示:
-
我说的必须第一本那是真的必须读的第一本,这本会告诉你,CPU 体系结构到底是什么样子的,即便是它一本书,有 300 多月页,也不够告诉你细节
-
3 本圣经是必读的
- ① 偏硬件部分
- ② 偏接口部分
- ③ 偏 OS 不分
- 这 3 本先看 ①,②③看你们自己了,我先看的 ②
-
必备的部分是有益的扩展,前 2 本是圣经的简单版本,先看这 2 本也可以的,再看圣经,后 2 本带你完全了解指令集,指令集才是我们学习的核心
-
其他里这几本,有时间看看,专业性不如上面,上面的不是圣经,也是龙芯团队写的