跳转至

系统基础

一段程序从来不只运行在某一层。源代码经过编译器变成指令,指令在乱序核心中推进,数据穿过寄存器、缓存、内存与设备,内核负责地址空间、调度与 I/O,语言运行时又在这些机制之上恢复更安全的抽象。系统学习的主线,正是理解这些层之间的契约与成本。

从哪里开始

  1. 系统抽象与性能度量:先学会区分规范、实现和观测,再建立延迟、吞吐、利用率、并行度与排队之间的定量关系。
  2. 数据表示与内存层次:理解同一组比特怎样成为整数、浮点数、指针与指令,以及局部性为何决定现代程序的大量性能差异。
  3. ISA 与微架构:把软件可见的指令契约和处理器内部实现分开。
  4. 进程与线程:观察内核如何把地址空间、执行流和资源引用组合成可调度实体。
  5. 并发的语义基础:从 happens-before 与所有权出发,而不是从“该选哪一种锁”出发。

一张跨层地图

层次 主要契约 常见隐藏成本 首要观测手段
语言与运行时 类型、对象生命周期、异常与并发语义 分配、边界检查、GC、调度 编译器报告、运行时 trace
编译器与 ABI 调用约定、布局、优化后的程序等价性 向量化失败、寄存器溢出、间接调用 IR、反汇编、优化备注
ISA 指令、寄存器、异常、内存访问语义 指令数不是周期数 PMU、硬件 trace
微架构 实际取指、执行、缓存与预测 miss、错误预测、端口争用 性能计数器、微基准
操作系统 进程、虚拟内存、文件、调度与隔离 缺页、上下文切换、回写、排队 perf、eBPF、/proc
设备与互连 队列、DMA、一致性与完成通知 传输、批处理、NUMA、队头阻塞 设备计数器、tracepoint

任何表格中的一格都不是孤岛。例如,一次看似普通的 C++ 数组访问,可能同时受对象布局、编译器别名分析、向量 ISA、缓存行、页表、NUMA 放置和调度迁移影响。定位问题时应沿数据和控制流逐层缩小范围,而不是先猜一个“经典原因”。

三条阅读纪律

先写清工作负载

性能没有脱离工作负载的绝对值。至少记录输入规模与分布、并发度、读写比例、热身策略、持续时间、软硬件版本和功耗策略。相同程序在冷缓存、热缓存、内存压力或频率受限时回答的是不同问题。

先验证语义,再优化路径

优化不能越过语言、ISA 或内核的契约。数据竞争、越界、失效的对象生命周期和错误的持久化假设,可能在某台机器上“恰好工作”,但不能由一次测量升级为保证。

用模型提出假设,用计数器证伪

模型把复杂系统压缩为少数关键变量;计数器和 trace 用来判断哪些变量真的控制了结果。模型不是装饰公式,计数器也不是指标收藏。两者必须围绕同一问题闭环:

\[ \text{现象}\rightarrow\text{假设}\rightarrow\text{可观测预测}\rightarrow\text{实验}\rightarrow\text{修正模型} \]

学习闭环

每个主题都可以用同一组问题检查:

  • 它解决了什么资源或语义矛盾?
  • 对上层承诺什么,对下层依赖什么?
  • 正常路径与慢路径的状态如何变化?
  • 正确性依赖哪些顺序、所有权和生命周期不变量?
  • 成本随输入、并发和硬件怎样缩放?
  • 哪些失败模式只在压力、崩溃或弱内存序下出现?
  • 怎样通过一个可复现实验区分相邻假设?

Reference