English
Computer architecture explains how instructions are executed and how that execution uses time, storage, and energy. Begin with a simple operation: adding two values requires obtaining the operands, performing the addition, and making the result available. A fast adder cannot help while its operands are still arriving from memory. Latency measures how long one operation takes; throughput measures how many operations finish per unit time. Overlapping independent work can improve throughput without reducing each operation’s latency.
Keep three levels separate. Digital logic treats electrical signals as discrete values within engineered tolerances. Adding two one-bit numbers gives a small example: the sum bit is x XOR y, which is 1 exactly when the inputs differ; the carry bit is x AND y, which is 1 when both inputs are 1. Thus 1 + 1 produces binary 10. Circuits for wider addition must also account for carries from less significant positions. Registers retain bits between steps, allowing later operations to use earlier results.
The instruction-set architecture (ISA) specifies programmer-visible operations, registers, exceptions, and memory behavior. Microarchitecture implements that contract through pipelines, execution units, caches, and other mechanisms. Two processors can support the same ISA while differing greatly in performance. Binary compatibility also depends on operating-system interfaces, execution modes, and available features; an ISA name alone does not guarantee that an old executable will run.
Hardware and software meet at these interfaces. Operating systems (§4.2) use privilege levels, address translation, interrupts, and atomic operations. Compilers (§4.7) select and arrange instructions. Architecture supplies a cost model for interpreting performance measurements, while workload, algorithms, and system configuration determine which costs dominate.
Prerequisites: Programming (§2.1) — you need to know what programs do before understanding how hardware executes them. Discrete mathematics (§2.2) — Boolean logic, number representations, and reasoning about state machines. Algorithms (§2.6) — cost analysis becomes concrete when you understand where the costs come from.
From Relay Calculators to Speculative Execution
Mechanical calculators preceded electronic computers. In the 1940s, storing encoded instructions in addressable memory made changing a computation less dependent on rewiring or external sequencing equipment. Programs could select their next operation through control flow and could themselves be represented as data. This idea helped connect hardware organization to software tools such as loaders and compilers.
Von Neumann’s 1945 EDVAC draft described ideas developed with Eckert, Mauchly, and other collaborators. The familiar stored-program model separates processing from storage and explains execution as fetching, decoding, and carrying out instructions. It is a useful starting model, not a requirement that every instruction access main memory directly or that modern processors execute only one instruction at a time. Instruction and data caches can be physically separate while supporting a unified programmer-visible address space.
Pipelining overlaps stages of successive instructions. Early machines such as IBM Stretch explored overlapping instruction work; the 8086 overlapped fetching with execution using a prefetch queue. Later designs developed more elaborate pipelines. Dependencies, branches, and competition for functional units can prevent stages from advancing as planned. Forwarding, stalling, prediction, and recovery handle different kinds of hazards. More stages can support a higher clock frequency but also increase overhead and recovery cost.
RISC research in the 1970s and 1980s studied how an instruction set could support efficient compiler-generated code and pipelined implementations. IBM’s 801, Patterson’s Berkeley RISC work, and Hennessy’s Stanford MIPS work were distinct projects. Load/store designs and simpler instruction formats eased some implementation tasks, sometimes requiring more instructions for the same work. The relevant comparison was total execution time, not instruction count alone. CISC and RISC labels do not predict modern performance by themselves: decoding, caching, scheduling, vector operations, and compiler choices all matter.
Superscalar processors can issue more than one instruction per cycle. They may issue instructions in order or out of order; superscalar execution does not itself require out-of-order scheduling. Tomasulo’s algorithm, developed for the IBM System/360 Model 91, helped establish techniques for tracking available operands and avoiding false register dependencies. Register renaming separates reused architectural register names from the physical locations holding their values. True dependencies remain: an instruction cannot calculate from a result that has not yet been produced.
Branch prediction lets instruction fetching continue before a branch outcome is known. A wrong prediction discards work from the incorrect path and redirects execution. The penalty depends on where the branch resolves and how much work can overlap; it is not simply the number of pipeline stages. Predictors use branch history and other context, and their effectiveness depends on the program. Speculation is useful because many predictions are correct, but its cost and side effects must be included in the design.
By the mid-2000s, power and heat increasingly constrained frequency scaling. A useful approximation for dynamic switching power is P ≈ αCV²f, where α describes switching activity, C effective capacitance, V voltage, and f frequency. Leakage contributes additional power. Raising frequency may require raising voltage, but power is not universally the cube of voltage. Transistor improvements therefore could no longer be translated as easily into higher clock rates for a single core.
Multicore designs use additional transistors to execute several instruction streams. Four cores at 2 GHz are not generally equivalent to one core at 8 GHz: software needs independent work, and cores share memory bandwidth and other resources. If 20% of a job must remain serial, even infinitely many ideal cores give at most a fivefold speedup. This is Amdahl’s law, developed further in §4.11. The shift toward multicore made workload decomposition, synchronization, and data placement more important to performance.
Spectre and Meltdown, disclosed in 2018, showed that architecturally discarded execution can still leave observable microarchitectural effects. Spectre can misdirect a victim’s speculative execution into revealing information through a side channel; Meltdown exploits particular implementations’ handling of protected loads. They are not the same failure mechanism. Cache timing is one observation channel, and mitigations depend on the variant and hardware. The Linux Spectre documentation illustrates why the security argument must include microarchitectural behavior, rather than assuming that an instruction’s failure to retire removes all its effects.
Specialized accelerators offer another way to spend a transistor and energy budget. GPUs support many parallel threads; tensor accelerators concentrate resources on selected numerical operations. General-purpose cores continue to improve, while accelerators benefit workloads that fit their operations and memory organization. Moving data to the device, launching work, and returning results also take time. The software problem is to expose enough useful parallel work without spending the gain on communication.
The Memory Hierarchy, Performance, and the Cost of Computation
A performance explanation should connect work to a limiting resource. Ask how much arithmetic is needed, how much data moves through each level, what operations depend on earlier results, and which activities can overlap. Different answers call for different optimizations.
Memory Hierarchy and Data Movement
Registers, caches, and main memory offer different combinations of capacity, access latency, bandwidth, and energy cost. Registers are normally named explicitly by instructions; caches usually keep copies of recently used memory automatically. The exact sizes and access times depend on the processor. A cache miss may delay a dependent instruction while other independent work proceeds, so miss count alone does not determine elapsed time.
Locality explains why caching helps. Temporal locality means reusing the same data; spatial locality means using nearby data. Suppose a cache line holds 64 bytes and elements occupy 8 bytes. A sequential traversal can use eight elements from a fetched line; scattered accesses may use only one. Neighboring accesses are not free, but they can avoid fetching another line from a slower level.
For an illustrative single-level cache, a 2 ns lookup, a 5% miss rate, and an additional 80 ns miss penalty give average access time 2 + 0.05×80 = 6 ns. This simple model assumes serialized accesses and fixed costs. Real processors can overlap misses, so measurements must also consider memory-level parallelism and bandwidth.
Data layout changes how much reuse is possible. Matrix tiling works on blocks that fit in fast storage, reusing loaded values before eviction. A linked traversal may need one fetched pointer before it knows the next address; independent hash lookups can sometimes overlap even when individual accesses are irregular. A good hash function distributes keys but does not itself guarantee locality.
In NUMA systems, access cost depends on which memory node holds the data and which core uses it. Placement, thread migration, and contention can therefore change performance without changing the algorithm. Measure the actual workload before choosing replication, partitioning, or affinity policies.
Pipeline and Execution: Where Performance Is Made and Lost
Pipelining overlaps stages of different instructions; superscalar issue starts multiple instructions in a cycle; out-of-order scheduling executes ready work before older work that is stalled. These mechanisms address different sources of unused execution capacity and can be combined.
Dependencies limit the benefit. If each addition takes three cycles and the next requires its result, the chain advances no faster than one addition every three cycles. Several independent accumulators may allow a processor to use more execution units, although changing the grouping of floating-point additions can change rounding. Instruction latency, issue throughput, and program dependencies must be considered separately.
A branch misprediction wastes work and delays useful instructions. Replacing a branch with unconditional computation may help, but may also perform unnecessary loads or expensive operations. Sorting data can make a later branch more predictable, yet sorting itself costs time. Compare total work, including any preprocessing, rather than timing only the favorable loop.
Prefetching and memory-level parallelism can hide some access latency. A sequential scan may supply many independent requests and become bandwidth-limited. A dependent pointer chain exposes less parallelism. A prefetch that arrives too early, too late, or for unused data can waste bandwidth and cache capacity. These are hypotheses to test with a profiler and controlled inputs.
Parallelism: Cores, Vectors, and Accelerators
Parallelism can occur within an instruction, between instructions, between cores, or across accelerators. The appropriate level depends on the work available and the cost of moving data and coordinating results.
Multiple cores can execute independent threads. Cache coherence coordinates copies of a cache line, while a memory-consistency model specifies permitted ordering of memory operations across locations. Coherence does not make an unsynchronized increment atomic. False sharing occurs when cores repeatedly write different variables on the same line, causing coherence traffic despite the variables being logically independent. Layout changes can help, but synchronization correctness must be established separately.
SIMD instructions apply an operation to several lanes of values. For example, a 256-bit vector can hold eight 32-bit floating-point values; x86 AVX provides arithmetic on such vectors. The number of lanes describes an instruction’s data organization, not necessarily one-cycle completion. Compilers can vectorize loops when dependencies and language rules permit it. Irregular accesses and branches may require gathers, masks, or extra work, so inspect both legality and measured benefit.
GPUs schedule many threads to keep execution units busy while other threads wait. Groups of threads execute under a shared instruction organization; NVIDIA calls these groups warps, while other architectures have different terminology and widths. Divergent control flow and inefficient memory accesses can reduce utilization. Dense numerical work often fits well, but small jobs may lose their benefit to launch and transfer costs. CUDA, ROCm, Metal, and SYCL expose different programming interfaces; performance still depends on the actual device and workload.
What Studying This Changes
Architecture gives you testable explanations for performance. Start with elapsed time and throughput, then ask whether the limiting factor is dependent computation, memory latency, bandwidth, synchronization, or input/output. One arithmetic operation per streamed element can be bandwidth-limited; operation count alone does not identify the bottleneck.
Compare sequential traversal with dependent pointer traversal, and measure a loop before and after changing its data layout. Inspect generated assembly when you need to check vectorization or instruction selection, and use hardware counters to test the explanation. State the processor, compiler options, data size, and cache conditions so that another reader can interpret the measurement.
The same model also helps identify security boundaries. Architectural permissions, speculative activity, and observable timing are different properties. A correct functional result is not by itself evidence that execution reveals no information through a side channel.
Resources
Books and Texts
Bryant and O’Hallaron’s Computer Systems: A Programmer’s Perspective connects machine-level code, memory, linking, and concurrency to software behavior. Patterson and Hennessy’s Computer Organization and Design adds datapaths, control, and pipeline implementation; choose an ISA edition that matches your exercises. Computer Architecture: A Quantitative Approach develops quantitative comparisons at greater depth. Drepper’s What Every Programmer Should Know About Memory offers a detailed memory-system perspective, with processor-specific advice to check against the hardware in use.
| Book | Role | Type |
|---|---|---|
| Bryant & O’Hallaron, Computer Systems: A Programmer’s Perspective (3rd ed.) | Systems from a programmer’s perspective | Entry |
| Patterson & Hennessy, Computer Organization and Design (RISC-V ed.) | Hardware-oriented entry | Entry |
| Hennessy & Patterson, Computer Architecture: A Quantitative Approach (6th ed.) | Graduate-level reference | Reference |
| Drepper, What Every Programmer Should Know About Memory (free) | Memory hierarchy at depth | Depth |
| Sorin, Hill & Wood, A Primer on Memory Consistency and Cache Coherence (paid / library) | Memory consistency and cache coherence at depth | Depth |
Courses and Lectures
CMU 15-213 pairs systems concepts with exercises in binaries, caches, and system programming. Berkeley CS 61C emphasizes machine representation and architecture. MIT 6.004 develops digital logic and processor construction. Choose exercises for the question you want to answer; course offerings and lab sequences vary by year.
| Course | Platform | Type |
|---|---|---|
| CMU 15-213 Introduction to Computer Systems (free) | CMU / YouTube | Entry |
| Berkeley CS 61C Computer Architecture (free) | Berkeley / YouTube | Entry |
| MIT 6.004 Computation Structures (free) | MIT OCW / edX | Depth |
Practice, Tools, and Current Sources
Use Compiler Explorer to inspect generated instructions, Cachegrind to study a simulated cache model, and perf for supported measurements on real hardware. Simulated misses and hardware events are not interchangeable, and event meanings vary by processor. A small pipeline simulator can make hazards and forwarding explicit; instruction-set simulators such as Spike or RARS do not automatically model pipeline timing. Agner Fog, uops.info, and vendor manuals supply processor-specific reference data.
| Resource | Platform | Type |
|---|---|---|
| Godbolt Compiler Explorer (free) | godbolt.org | Practice |
| Cachegrind / Valgrind (free) | valgrind.org | Practice |
| perf (Linux, free) | Linux kernel tools | Practice |
| Agner Fog optimization manuals (free) | agner.org | Reference |
| uops.info instruction tables (free) | uops.info | Reference |
| Intel 64 and IA-32 Optimization Reference Manual (free) | intel.com | Reference |
| Chips and Cheese microarchitecture analyses (free) | chipsandcheese.com | Auxiliary |
| RISC-V pipeline simulator project | Local | Practice |
Traps
| Trap | Why it misleads | Better response |
|---|---|---|
| Stopping at logic gates | Gate-level design explains building blocks, while instruction execution and memory organization answer further questions. | Trace one operation from instruction through operands and result, then study the relevant hardware level. |
| Equating ISA with implementation | The same programmer-visible contract can have different implementations and costs. | Compare dependency chains, issue throughput, caches, and memory behavior on a named processor. |
| Assuming every program is memory-bound | Some workloads are limited by computation, dependencies, synchronization, or I/O. | Measure before choosing cache tiling, vectorization, or another optimization. |
| Ignoring generated code | Source syntax does not show the instructions or transformations actually used. | Inspect a small optimized function in Compiler Explorer and compare it with measured behavior. |
| Ignoring microarchitectural security | Architecturally discarded work can leave observable effects. | Distinguish architectural permission, speculation, and side-channel observation. |
| Memorizing universal latency numbers | Costs vary with processor, configuration, contention, and workload. | Use numbers as an explicit model, then consult target documentation and measurements. |
中文
计算机体系结构解释指令怎样执行,以及执行怎样使用时间、存储和能量。可以从简单加法开始:取得操作数,完成加法,再使结果可用。如果操作数仍在从内存传来,更快的加法器也无法工作。延迟衡量一次操作需要多久,吞吐量衡量单位时间完成多少操作。重叠执行独立工作,可以提高吞吐量,却不一定缩短每次操作的延迟。
区分三个层次。数字逻辑在工程容差内把电信号当作离散值。两个一位二进制数相加就是一个小例子:和位为 x XOR y,恰好在输入不同时为 1;进位为 x AND y,在两个输入都为 1 时为 1。因此,1 + 1 产生二进制 10。处理更多位的加法电路还必须考虑较低位传来的进位。寄存器则在步骤之间保存比特,让后续操作能够使用先前的结果。
指令集架构(ISA)规定程序可见的操作、寄存器、异常与内存行为;微架构通过流水线、执行单元、缓存等机制实现这个约定。两个处理器可以支持同一 ISA,却具有很不相同的性能。二进制兼容性还依赖操作系统接口、执行模式和可用特性,只有相同 ISA 名称并不保证旧可执行文件能够运行。
硬件与软件在这些接口上连接。操作系统(§4.2)使用权限级别、地址转换、中断与原子操作,编译器(§4.7)选择并安排指令。体系结构提供解释性能测量的成本模型,而工作负载、算法与系统配置共同决定哪些成本占主要部分。
前置知识:编程(§2.1)——你需要先知道程序做什么,才能理解硬件如何执行它们。离散数学(§2.2)——布尔逻辑、数值表示,以及对状态机的推理。算法(§2.6)——当你理解成本来自哪里时,成本分析才会变得具体。
从继电器计算器到推测执行
机械计算器早于电子计算机。二十世纪四十年代,把编码后的指令放入可寻址存储,使改变计算较少依赖重新接线或外部控制设备。程序可以通过控制流选择下一步操作,本身也可以被表示为数据。这种思想把硬件组织连接到加载器、编译器等软件工具。
冯·诺依曼在 1945 年的 EDVAC 报告草稿中,描述了与 Eckert、Mauchly 等人共同发展的思想。常见的存储程序模型区分处理与存储,用取指、译码、执行说明运行过程。这是有用的起始模型,不要求每条指令直接访问主存,也不要求现代处理器一次只执行一条指令。指令缓存与数据缓存可以物理分离,同时支持程序可见的统一地址空间。
流水线重叠相邻指令的执行阶段。IBM Stretch 等早期机器探索指令工作的重叠,8086 使用预取队列重叠取指与执行,后来的设计发展了更复杂的流水线。依赖、分支与功能单元争用,可能阻止阶段按计划推进。转发、停顿、预测与恢复处理不同冒险。更多阶段可以支持较高频率,也会增加额外开销与恢复成本。
二十世纪七八十年代的 RISC 研究,探讨指令集怎样支持高效的编译器生成代码和流水线实现。IBM 801、Patterson 的 Berkeley RISC 与 Hennessy 的 Stanford MIPS 是不同项目。访存与运算分离的设计、较简单的指令格式,简化了部分实现工作,有时也需要更多指令完成相同任务。需要比较的是总执行时间,而不只是指令数。仅凭 CISC 与 RISC 标签,无法预测现代性能;译码、缓存、调度、向量操作和编译器选择都很重要。
超标量处理器能够在一个周期发射多条指令,可以采用顺序或乱序发射;超标量本身不要求乱序调度。为 IBM System/360 Model 91 开发的 Tomasulo 算法,帮助建立了跟踪操作数就绪情况、避免寄存器伪依赖的技术。寄存器重命名把反复使用的架构寄存器名称,与实际保存各个值的物理位置分开。真实依赖仍然存在:指令无法用尚未产生的结果进行计算。
分支预测允许处理器在尚不知道分支结果时继续取指。预测错误时,需要丢弃错误路径上的工作并重新定向执行。代价取决于分支在哪里确定,以及多少工作可以重叠,并不简单等于流水线级数。预测器使用分支历史和其他上下文,其效果依赖程序。推测执行之所以有用,是因为许多预测正确,但设计也需要考虑其成本与副作用。
到二十一世纪初期,功耗与散热越来越限制频率提升。动态开关功耗的一个有用近似是 P ≈ αCV²f,其中 α 表示开关活动,C 为有效电容,V 为电压,f 为频率。漏电还会产生额外功耗。提高频率可能需要提高电压,但功耗并不普遍等于电压的三次方。因此,晶体管的改进不再能像过去那样容易地转化为单核心的更高时钟频率。
多核心设计使用更多晶体管执行多条指令流。四个 2 GHz 核心一般不等于一个 8 GHz 核心:软件需要独立工作,核心还共享内存带宽等资源。如果任务有 20% 必须串行执行,即使理想地拥有无限多核心,也至多加速五倍。这就是 §4.11 将进一步解释的 Amdahl 定律。向多核心发展的过程,使任务分解、同步和数据放置更直接地影响性能。
2018 年披露的 Spectre 与 Meltdown 表明,在架构层被丢弃的执行,仍可能留下可观察的微架构影响。Spectre 可以误导受害程序的推测执行,使其通过侧信道泄露信息;Meltdown 利用的是特定实现处理受保护读取的方式。两者不是同一种失败机制。缓存时序是一种观察渠道,缓解措施依赖变体与硬件。Linux 的 Spectre 文档说明,安全论证需要包含微架构行为,不能认为指令未提交就消除了全部影响。
专用加速器提供了分配晶体管与能量预算的另一种方式。GPU 支持大量并行线程,张量加速器则把资源集中于选定的数值操作。通用核心仍在改进,而加速器有利于符合其运算和存储组织的工作负载。向设备传送数据、启动任务、返回结果也需要时间。软件需要提供足够多的有效并行工作,同时避免通信耗尽收益。
内存层级、性能与计算成本
性能解释应把工作量连接到限制资源:需要多少算术操作,每个层次传输多少数据,哪些操作依赖前面的结果,以及哪些活动可以重叠。不同答案对应不同优化方法。
存储层次与数据移动
寄存器、缓存和主存提供不同的容量、访问延迟、带宽与能耗组合。指令通常显式指定寄存器;缓存一般自动保留最近使用过的内存副本。确切大小与访问时间依赖处理器。缓存未命中可能延迟依赖该数据的指令,而其他独立工作仍然继续,因此仅凭未命中次数不能确定运行时间。
局部性解释了缓存为何有效。时间局部性表示重复使用相同数据,空间局部性表示使用邻近数据。假设缓存行为 64 字节,每个元素占 8 字节,顺序遍历可以使用已取入行中的八个元素;分散访问可能只使用其中一个。邻近访问并非免费,但可以避免从较慢层次再取入一行。
对于示意性的单级缓存,如果查询耗时 2 ns、未命中率 5%、未命中额外代价 80 ns,平均访问时间为 2 + 0.05×80 = 6 ns。这个简单模型假设串行访问和固定成本。真实处理器可以重叠未命中访问,因此测量还要考虑内存级并行与带宽。
数据布局改变可用的复用。矩阵分块处理能装入快速存储的小块,在数据被替换前重复使用它们。链式遍历可能必须先取得一个指针,才知道下一个地址;独立的哈希查询则有时能够重叠,即使单次访问不规则。良好的哈希函数分散键,却不自动保证局部性。
在 NUMA 系统中,访问成本取决于数据位于哪个内存节点,以及哪个核心使用它。因此,即使算法不变,放置、线程迁移和争用也可以改变性能。选择复制、分区或亲和性策略之前,应测量实际工作负载。
流水线与执行:性能被创造和丢失的地方
流水线重叠不同指令的执行阶段,超标量发射在一个周期启动多条指令,乱序调度则让已就绪工作先于更早但受阻的工作执行。这些机制处理不同来源的执行资源闲置,也可以组合使用。
依赖关系限制收益。如果每次加法需要三个周期,下一次又依赖其结果,那么这条链至多每三个周期推进一次。多个独立累加器可能让处理器利用更多执行单元,不过改变浮点加法的分组也可能改变舍入结果。需要分开考虑指令延迟、发射吞吐量与程序依赖。
分支预测错误会浪费工作并延迟有效指令。用无条件计算替换分支可能有帮助,也可能引入不必要的读取或昂贵操作。排序可以使后续分支更容易预测,但排序本身也花时间。应比较包含预处理的总工作,而不只计时有利的循环。
预取与内存级并行可以隐藏部分访问延迟。顺序扫描可能提供许多独立请求,转而受带宽限制;依赖指针链暴露的并行性较少。到达太早、太晚,或取入未使用数据的预取,会浪费带宽和缓存容量。这些判断需要用性能分析器和受控输入检验。
并行性:核心、向量与 Accelerators
并行可以发生在一条指令内部、指令之间、核心之间,或加速器之间。采用哪个层次,取决于可用工作以及移动数据、协调结果的成本。
多个核心可以执行独立线程。缓存一致性协调同一缓存行的副本,内存一致性模型则规定跨位置内存操作允许的顺序。缓存一致性不会使没有同步的递增变成原子操作。当不同核心反复写同一行中的不同变量时,即使变量在逻辑上独立,也会产生一致性流量,这称为伪共享。调整布局可能有帮助,但同步正确性需要另行建立。
SIMD 指令对多个通道的值应用操作。例如,256 位向量可以容纳八个 32 位浮点值,x86 AVX 提供这类向量算术。通道数描述指令的数据组织,不必然表示一个周期完成。依赖与语言规则允许时,编译器可以向量化循环。不规则访问与分支可能需要聚集加载、掩码或额外工作,因此要同时检查合法性与测量收益。
GPU 调度大量线程,使一部分线程等待时执行单元仍有工作。线程组在共享的指令组织下执行;NVIDIA 把这种组称为 warp,其他架构具有不同术语和宽度。分歧控制流和低效访存会降低利用率。密集数值工作常常适合 GPU,小任务却可能被启动与传输成本抵消收益。CUDA、ROCm、Metal 和 SYCL 提供不同编程接口,性能仍依赖具体设备与工作负载。
学习这一部分会改变什么
体系结构提供可以检验的性能解释。先观察运行时间与吞吐量,再判断限制来自依赖计算、内存延迟、带宽、同步还是输入输出。逐元素流式计算即使每个元素只有一次算术操作,也可能受带宽限制;操作数本身不能识别瓶颈。
比较顺序遍历与依赖指针遍历,并测量改变数据布局前后的循环。需要检查向量化或指令选择时,查看生成的汇编,再用硬件计数器检验解释。记录处理器、编译选项、数据大小与缓存条件,使他人能够解释测量结果。
同一模型也帮助识别安全边界。架构权限、推测活动与可观察时序是不同性质。功能结果正确,本身不能证明执行没有通过侧信道泄露信息。
资源
书籍与文本
Bryant 与 O’Hallaron 的 Computer Systems: A Programmer’s Perspective 把机器级代码、内存、链接与并发连接到软件行为。Patterson 与 Hennessy 的 Computer Organization and Design 补充数据通路、控制与流水线实现;应选择与练习匹配的 ISA 版本。Computer Architecture: A Quantitative Approach 更深入地展开定量比较。Drepper 的 What Every Programmer Should Know About Memory 提供详细的存储系统视角,其中针对具体处理器的建议需要对照正在使用的硬件检查。
| 书籍 | 作用 | 类型 |
|---|---|---|
| Bryant & O’Hallaron, Computer Systems: A Programmer’s Perspective(第 3 版) | 从程序员视角理解系统 | 入门 |
| Patterson & Hennessy, Computer Organization and Design(RISC-V ed.) | 硬件导向入口 | 入门 |
| Hennessy & Patterson, Computer Architecture: A Quantitative Approach(第 6 版) | 研究生层级参考 | 参考 |
| Drepper, What Every Programmer Should Know About Memory(免费) | 深入内存层级 | 深入 |
| Sorin, Hill & Wood, A Primer on Memory Consistency and Cache Coherence(付费 / 图书馆) | 深入内存一致性与 cache coherence | 深入 |
课程与讲座
CMU 15-213 把系统概念与二进制、缓存及系统编程练习结合。Berkeley CS 61C 强调机器表示与体系结构,MIT 6.004 展开数字逻辑与处理器构造。应按想回答的问题选择练习;课程安排与实验序列会随年份变化。
| 课程 | 平台 | 类型 |
|---|---|---|
| CMU 15-213 Introduction to Computer Systems(免费) | CMU / YouTube | 入门 |
| Berkeley CS 61C Computer Architecture(免费) | Berkeley / YouTube | 入门 |
| MIT 6.004 Computation Structures(免费) | MIT OCW / edX | 深入 |
实践、工具与当前资料
用 Compiler Explorer 检查生成指令,用 Cachegrind 研究模拟缓存模型,用 perf 取得真实硬件支持的测量。模拟未命中与硬件事件不可互换,事件含义也随处理器变化。小型流水线模拟器能使冒险与转发变得明确;Spike、RARS 等指令集模拟器不会自动模拟流水线时序。Agner Fog、uops.info 与厂商手册提供具体处理器的参考数据。
| 资源 | 平台 | 类型 |
|---|---|---|
| Godbolt Compiler Explorer(免费) | godbolt.org | 实践 |
| Cachegrind / Valgrind(免费) | valgrind.org | 实践 |
| perf(Linux,免费) | Linux kernel tools | 实践 |
| Agner Fog optimization manuals(免费) | agner.org | 参考 |
| uops.info instruction tables(免费) | uops.info | 参考 |
| Intel 64 and IA-32 Optimization Reference Manual(免费) | intel.com | 参考 |
| Chips and Cheese microarchitecture analyses(免费) | chipsandcheese.com | 辅助 |
| RISC-V pipeline simulator project | 本地 | 实践 |
陷阱
| 误区 | 为什么会误导 | 更好的做法 |
|---|---|---|
| 学完逻辑门就停止 | 门级设计解释基础组件,指令执行与存储组织还回答其他问题。 | 跟踪一次操作从指令到操作数与结果,再学习相关硬件层次。 |
| 把 ISA 等同于实现 | 相同的程序可见约定,可以具有不同实现与成本。 | 在明确的处理器上比较依赖链、发射吞吐量、缓存与内存行为。 |
| 假设所有程序都受内存限制 | 有些工作负载受计算、依赖、同步或 I/O 限制。 | 测量之后,再选择缓存分块、向量化或其他优化。 |
| 忽略生成代码 | 源语法不显示实际采用的指令与变换。 | 在 Compiler Explorer 检查优化后的小函数,并与测量行为比较。 |
| 忽略微架构安全 | 架构层丢弃的工作可能留下可观察影响。 | 区分架构权限、推测执行与侧信道观察。 |
| 背诵普遍适用的延迟数字 | 成本依处理器、配置、争用与工作负载变化。 | 把数字作为明确模型,再查阅目标文档与测量结果。 |