第1章 · 趣味学习

计算机组成与体系结构:拆开电脑看个究竟

"饿了么么"外卖App刚上线三天,服务器就卡成了PPT。老王一拍桌子:"再买台机器!堆就完了!"小明翻了个白眼:"你连CPU是怎么取指令执行都不知道,堆一万台也是烧钱。"老王不服:"我又不是修电脑的,架构师干嘛要懂硬件?"

大牛哥端着咖啡路过:"架构师不懂底层原理,就像厨师不知道火是怎么着的——菜炒糊了只能怪锅。这章就带你把电脑拆开,看看里面到底装了啥、怎么跑的、为啥会卡。"

🧱 1.1 计算机系统组成:硬件全家桶

计算机系统是硬件+软件的综合体,按功能分成多级层次。原始的冯·诺依曼计算机以运算器为中心,现代计算机已经转向以存储器为中心——因为现在干活最慢的、最值钱的、最拖后腿的都是存数据。

1.1.1 硬件六大件

老王
电脑不就是CPU+内存+硬盘嘛,还能有啥?
小明
六大件呢。记不住的话,想想你收快递:眼睛看单号(输入)、脑子算账(运算器+控制器)、桌子暂存(主存)、仓库囤货(辅存)、嘴报数(输出)。还少一个?传送带本身就是总线。
部件 干啥的 关键零件
控制器分析执行指令的总指挥PC(下条指令地址) / IR(暂存指令) / ID(译码) / 时序部件
运算器 ALU算术+逻辑运算ALU / AC累加器 / DR数据缓冲 / PSW状态条件
主存储器存现场指令和数据(内存)CPU可直接读写
辅助存储器长期保存信息(外存)硬盘、U盘
输入设备把人的信息塞进电脑键盘、鼠标、扫描仪
输出设备把结果送出电脑显示器、打印机
💡 秒懂技巧:控制器是"大脑的指挥官",运算器是"算盘手",主存是"工作台"(手边随时取用),辅存是"仓库"(长期囤货)。PSW状态寄存器有个争议点——有的教材把它归控制器,做题时注意题干怎么说。

🏗️ 1.1.2 系统结构分类:从冯·诺依曼到Flynn

存储程序概念是冯·诺依曼1946年提出的,三句话:①五大部件组成;②内部用二进制;③程序和数据先存进存储器再启动。它最大的贡献是"存储程序控制",最大弱点是存储器访问成为瓶颈。突破这个瓶颈的就叫"非冯·诺依曼机"(数据流计算机、归约计算机等)。

1966年Flynn按指令流数据流的多倍性把计算机分成四类:

类型 指令流 数据流 举例/人话
SISD传统单处理器,一条指令一条数据地干
SIMD并行处理机/矩阵处理机,一条指令对一批数据同时算
MISD实际很少见,有文献把流水线算这类
MIMD多核处理器、多处理机,全面并行
💡 秒懂技巧:把"指令流"想成"喊口号的人有几个","数据流"想成"干活的人有几个"。SISD是一个人喊一个人干;SIMD是一个人喊一群人一起干同一件事(大合唱指挥);MIMD是一群人各喊各的各干各的(小组讨论)。

⚔️ 1.1.3 CISC vs RISC:复杂派 vs 精简派

老王
指令集不就是给CPU下命令嘛,越多越好啊,功能全!
小明
CISC就是这么想的,结果发现20%的指令占了80%的使用率,剩下那一堆复杂指令根本没人用,还把译码电路搞成一锅粥。RISC反其道而行——指令少而精,单周期搞定,流水线飞起。
对比项 CISC(复杂指令集) RISC(精简指令集)
指令数量100~250条少,只用高频简单指令
寻址方式5~20种少,寄存器/立即数/相对寻址
指令长度变长(译码复杂)固定,格式少(译码容易)
主存访问有指令直接操作主存只有LOAD/STORE访存,其余在寄存器间
控制方式微程序控制为主硬布线逻辑为主
执行多周期单周期+流水线
通用寄存器较少多,一般32个以上,有的上千
CISC像一把瑞士军刀——啥功能都塞进去,结果又重又贵,你日常也就用刀和剪刀那两样。RISC像一套专用厨刀——每把就干一件事,但单把轻快顺手,配合流水线(切菜的同时另一个人已开始炒)效率反而高。大多数RISC还用独立指令Cache+数据Cache,取指取数据两不耽误。

🛣️ 1.1.4 总线:数据的高速公路

总线是一组能为多个部件分时共享的公共信息传送线路。"共享"是多个部件都能挂上去,"分时"是同一时刻只允许一个部件发信息(否则撞车),但可以多个部件同时接收。

  • 按位置分:内部总线(CPU内寄存器/ALU之间)、外部总线(CPU与内存/外设之间)
  • 按功能分:地址总线(传地址)、数据总线(传数据)、控制总线(传控制信号)
总线像一条单车道公路:发车只能一辆一辆发(分时),但路边谁都能上下车(共享)。地址总线是"门牌号车道",决定能寻址多大空间;数据总线是"货车车道",越宽一次拉得越多。总线速度是制约整机性能的大头——CPU再快,路堵着也白搭。

💾 1.2 存储器系统:金字塔式的记忆术

存储器分三级:Cache → 主存 → 辅存。越往上越快越小越贵,越往下越慢越大越便宜。CPU直接访问主存,但不能直接访问辅存(要靠I/O搬进主存)。当CPU太快主存跟不上时,就在CPU和主存之间塞一层Cache。

这套能用低投入换高速度,靠的是局部性原理:程序执行时短期内只局限在某片区域。分两种——

  • ⏱️ 时间局部性:某条指令刚执行过,不久后可能再执行(典型:循环)
  • 🗺️ 空间局部性:访问了某单元,附近单元也很快被访问(典型:顺序执行)

存取方式有四种:顺序存取(磁带,按线性顺序)、直接存取(磁盘,直接定位到数据块)、随机存取(主存,任意单元等时访问)、相联存取(Cache,按内容而不是地址访问)。

💡 秒懂技巧:存储层次像你办公的记忆系统——Cache是桌上便签纸(手边常翻,写得快容量小),主存是本子(翻一翻就有,但要找),辅存是档案柜(容量大但要跑一趟)。局部性原理就是"你正在写的报告,相关的几页纸肯定都摊在桌上,不会一会儿翻刑法一会儿翻菜谱"。

🧩 1.2.1 主存储器:RAM、ROM和编址

主存分RAM(随机存取,可读写但断电丢数据)和ROM(只读,断电不丢,常存BIOS)。

  • DRAM(动态RAM):信息会随时间消失,需定时刷新;密度大、便宜、但慢
  • SRAM(静态RAM):不断电信息一直在;速度快、电路简单、但容量小、贵
老王
那内存编址是啥意思?地址还有讲究?
小明
每个8位是一个单元。按字编址还是按字节编址,算出来的容量不一样。比如地址AC000H到C7FFFH,C7FFFH−AC000H=1BFFFH个单元,十进制就是112KB。若按16位字编址,就是112KB×16位。这题考试常考。
编址题套路:先算地址单元数 = 末地址 − 首地址,再换算十进制。注意区分"按字节编址"(8位)和"按字编址"(字长如16/32位)。若问芯片每单元存几位,用总容量÷芯片数÷每片单元数。

💿 1.2.2 辅助存储器:磁带与硬盘

磁带是顺序存取,容量大、便宜、好携带,但慢,现在主要用于归档。硬盘信息分布层次:记录面 → 圆柱面(柱面)→ 磁道 → 扇区。

  • 每个盘片两个记录面,每个面对应一个磁头,所有磁头一起径向移动
  • 各记录面上相同编号的磁道构成一个柱面——引入柱面是为提高速度:大文件先塞满同一柱面,避免来回寻道
  • 一条磁道划分为若干扇区,每扇区存定长块(如512字节),扇区编号从1开始(磁头/柱面从0开始)

磁盘访问时间 = 寻道时间 + 旋转延迟时间(定位磁道+旋转到目标扇区,平均转半圈)。

硬盘像一摞唱片叠在一起:每张唱片两面都能放歌(记录面),唱针(磁头)搁在某圈上,所有唱针一起挪动。同一圈号在所有唱片上的总和叫"柱面"——存大文件时先把一整摞唱片的第5圈都写满,比一张张写完再换圈省事得多,省的就是"挪唱针"(寻道)的时间。

⚡ 1.2.3 Cache:突破冯·诺依曼瓶颈的便签纸

Cache的功能是提高CPU数据输入输出速率,突破"冯·诺依曼瓶颈"(CPU与存储系统间数据传送带宽限制)。它在CPU和内存之间设小容量高速存储,靠相联存储器CAM(按内容访问)实现。命中率高,系统性能就大幅提升。

设命中率 h,Cache周期 t1,主存周期 t2,则平均周期 t3 = t1·h + t2·(1−h)。命中率哪怕提高一点,性能也明显改善。

小明
大牛哥,主存100ns、Cache 10ns、取指命中率98%、取数命中率95%、约1/5指令要取操作数,平均每条指令访存时间咋算?
大牛哥
指令部分(2%×100+98%×10)ns + 操作数部分1/5×(5%×100+95%×10)ns = 11.8 + 2.9 = 14.7ns。命中率每降1%,时间就跳一截,所以Cache设计是门精细活。

三种映射方式

主存和Cache都分成同样大小的块。CPU访存时先查Cache有没有,这叫地址映射

方式 规则 优缺点
直接映像主存每页只能进Cache固定页(区号+页号+页内地址)硬件简单,但冲突率高、不灵活
全相联映像主存任意页可映像到Cache任意页灵活、冲突低,但比较器贵、速度慢,仅小容量Cache用
组相联映像组间直接映像、组内全相联(折中)兼顾灵活与代价,主流选择
💡 秒懂技巧:直接映像像"对号入座"——你的工位固定,哪怕你隔壁工位空着你也只能坐自己的;全相联像"随便坐"——大厅任意位都能坐,但得挨个问"这有人吗",找位慢;组相联像"分部门随便坐"——你只能坐自己部门的位,但部门里随便挑,兼顾效率与灵活。组内只有1页就退化成直接映像,组内=全部就退化成全相联。

替换算法:Cache满了踢谁走?

  • 🎲 随机算法:瞎选一个换掉,简单粗暴
  • 📤 FIFO(先进先出):按进Cache的先后淘汰最早的。简单,但可能踢掉正在用的循环块
  • 📉 LRU(近期最少使用):淘汰最近用得最少的,需"年龄计数器"记录。较合理但开销大

写操作:怎么保证Cache和主存一致?

  • ✍️ 写直达(write through):写Cache同时写回内存。简单、数据始终正确,但慢(多写主存)
  • 🔁 写回(write back):改了Cache先不写内存,等这块被淘汰时才写回。快,但主存可能短暂不一致(用标志位记录是否改过)
  • 🏷️ 标记法:每数据设有效位,进Cache置1,CPU改数据只写内存并把有效位置0。读时先查有效位,为1才从Cache取
🎮 实战场景:饿了么么高峰期订单刷屏,小明查Cache命中率发现只有70%——大量订单查询打到主存,主存100ns的延迟在每秒万级请求下就是灾难。优化方向:换组相联映像降低冲突、调LRU淘汰策略、把热点商家数据预取进Cache。命中率从70%提到95%,平均访问时间能从37ns降到13ns——三倍提升,比加机器便宜多了。

🌊 1.3 流水线:像工厂流水线一样干活

流水线把一个任务分解成若干子任务,不同子任务由不同机构并行执行——任一时刻每个机构只处理一个任务的不同阶段,实现重叠执行提高效率。

⏱️ 1.3.1 流水线周期:木桶效应

把工作分N个阶段,最耗时的那段的时间就是流水线周期。比如取指2ms、分析4ms、执行1ms,流水线周期就是4ms——最慢的那段卡住所有人。

流水线周期是"瓶颈工位"的耗时。就像食堂打饭:盛饭2秒、打菜4秒、刷卡1秒——第2个工位最慢,后面的人就只能4秒4秒地往前挪。整条线的速度由最慢工位决定,跟木桶短板一个道理。

🧮 1.3.2 执行时间:理论 vs 实际

流水线执行时间 = 第1条指令的完整时间 + (n−1) × 流水线周期。但考试有两种算法:

小明
100条指令,取指2ms、分析4ms、执行1ms。理论算法:1条指令2+4+1=7ms,总时间=7+(100−1)×4=403ms。实际算法:把每段都统一成流水线周期4ms,1条=4×3=12ms,总时间=12+(100−1)×4=408ms。到底用哪个?
大牛哥
考试80%以上用理论公式。先按理论算,如果选项里没有理论值,再用实际公式。这是出题人的潜规则。
理论公式:各段实际耗时之和 + (n−1)×流水线周期。实际公式:各段都按流水线周期算(段数×周期)+ (n−1)×周期。先理论后实际,没有理论值选项再换实际。

🚀 1.3.3 吞吐率:单位时间出多少活

吞吐率 TP = 单位时间内完成的任务数 = 任务数 / 流水线总时间。流水线周期越短、段数越合理,吞吐率越高。

吞吐率就是食堂"每分钟能端出几份饭"。流水线建立后,每个周期(4ms)就出一条指令,吞吐率≈1/周期。但启动阶段(第1条指令)不出活,所以任务越多,启动开销被摊薄,吞吐率越接近峰值1/周期。

🏁 1.3.4 加速比:用了流水线快多少

加速比 = 不使用流水线的时间 / 使用流水线的时间 = T0 / Tk。如果各段时间相等(设为Dt),一条k段流水线完成n个连续任务:流水线时间=(k+n−1)Dt,顺序时间=nkDt,加速比=nk / (k+n−1)。任务数n越大,加速比越接近k(段数)——加再多段,收益也是有上限的。

💡 秒懂技巧:加速比上限是流水线段数k。10段流水线,任务无限多时最多快10倍,不可能快100倍。这就是阿姆达定律的雏形——后面第5章会正式讲它。加段不是无限划算的,段越多冲突和开销也越多。
🎮 实战场景:小明要优化订单处理流水线,原来顺序处理100个订单要700ms,改成3段流水线后403ms,加速比约1.74。想再快?把最慢的"分析4ms"拆细或者优化掉,瓶颈段不缩,其他段再快也白搭。