skip to content
[Jimjimu Notes]

Computer Organization: I/O and Parallelism

/ 45 min read

Table of Contents

Course index · Previous: Virtual Memory

I/O Polling

Pasted image 20260530164343

轮询机制

Pasted image 20260530164332

以及这里的andi是按位与,就是判断末位是否为1,为0就一直循环

Pasted image 20260530164728 Pasted image 20260530164739 Pasted image 20260530164747

polling一个disk会占用非常多的cpu时钟周期,所以我们对于从disk读取数据不应采用polling 轮询的效率不高

I/O Interrupts

Pasted image 20260530165305 Pasted image 20260530165255 Pasted image 20260530170044

在 PIO 模式下,外部设备和主内存(Main Memory)之间是不能直接讲话的。每一个字节的数据搬运,都必须由 CPU 亲自执行 lw (Load Word) 和 sw (Store Word) 指令来完成。 CPU 先把数据从外设读到自己的寄存器里,然后再写到内存里。

缺点:

  • 浪费 CPU 算力 (CPU has to execute all transfers…)
  • 严重的速度不匹配 (Device speeds don’t align…)
  • 极高的能耗代价 (Energy cost of using beefy general-purpose CPU…)

Direct Memory Access (DMA)

Pasted image 20260530170926 Pasted image 20260530170939

可以说DMA就是CPU雇的一个打工人,在上述过程中,CPU只会接受到两次Interrupt:开始传输、结束。在数据传输过程中,CPU可以做别的事情

Pasted image 20260530171257 Pasted image 20260530171308

Parallelism

Flynn Taxonomy(弗林分类法)

软件的设计方式与硬件的物理架构是完全独立的两个维度

Pasted image 20260530203734

硬件维度的并行:取决于物理上CPU有几个核心 软件维度的并行:取决于代码的写法

弗林分类法(Flynn’s Taxonomy)

它根据指令流(Instruction Stream)数据流(Data Stream)的数量,将计算机架构分为四类:

  • SISD (单指令单数据 - 对应上图的 Pentium 4)
  • SIMD (单指令多数据 - 比如 CPU 里的向量指令集 AVX,或者 GPU 的核心原理)
  • MISD (多指令单数据)
  • MIMD (多指令多数据 - 对应上图的 Core i7 等现代多核 CPU)
Pasted image 20260530204501

SIMD Architecture

Pasted image 20260530205831

当你的程序中有大量相同类型的数据,且需要对它们做一模一样的操作时,就存在数据级并行。(Data-Level Parallelism)

Pasted image 20260530205854

硬件将 4 个 32 位的数字(X0 到 X3)像装箱子一样“打包”装进这一个超长寄存器里

XMM register是一个大宽度寄存器

Pasted image 20260530211132 Pasted image 20260530211420 Pasted image 20260530211434

Multicore

为什么需要多核心? 为了提升perfrormance,但是提升时钟频率已经到头了

Pasted image 20260601182727 Pasted image 20260601182745 Pasted image 20260601183611 Pasted image 20260601183625

Thread

Thread:顺序执行的一系列指令流

Pasted image 20260601183835 Pasted image 20260601184350

硬件线程:它是 CPU 中真正能够拉取并执行指令的物理实体。它包含了真实的硅片电路,如运算器、物理寄存器堆等。

  • 数量限制:数量是非常有限且固定的。比如我们常说一台电脑是“8核16线程”(支持同步多线程/超线程技术),这就意味着这台机器在物理层面上,最多只能同时提供 16 个硬件线程。
  • 类比:它们就像是共享办公室里真实存在的“办公桌”,只有坐在办公桌前才能干活。

软件线程:它是操作系统(或应用程序)创建的一系列指令

Pasted image 20260601190553 Pasted image 20260601190604

Multithreading

Pasted image 20260601192236 Pasted image 20260601192301 Physical CPU就是传统我们理解的物理硬件CPU核心 Logical CPU:硬件线程,一个cpu核心能有超过一个硬件线程,所以说Logical CPU > Physical CPU Pasted image 20260601193739

logical threads就是上述的一个核心有多个硬件线程,因为它本质上是“填补物理核心的空闲时间”(比如等内存时切换线程),所以它不能让性能翻倍

Pasted image 20260601193757

OpenMP

一个C语言的扩展,用于处理

Pasted image 20260601201335 Pasted image 20260601201351 可以看到右侧的 `thread 0, i = 0` 之后紧跟着是 `thread 1, i = 3`。这正是多线程并发执行的经典现象。因为 4 个线程在物理核心上是独立且同时(或交替)运行的,它们谁先跑到 `printf` 这一行,谁就把字打在屏幕上。这种不可预测性提醒我们:**在多线程里,绝对不能依赖代码的物理顺序来假设执行顺序。** Pasted image 20260601201449

Example: Computing π\pi

Pasted image 20260602001511

为了避免上述的“更新丢失”,我们必须保证同一时刻只能有一个线程去修改 sum。如果程序员采用最原始的加锁机制(比如互斥锁 Mutex 或临界区 Critical Section)把 sum += ... 这行代码包起来:

  • 虽然答案算对了,但多线程每次循环到这里时,都必须排队,一个接一个地执行累加
  • 这就导致原本应该并行的代码,在这里变成了串行(顺序)执行,彻底抵消了多线程带来的性能优势。
Pasted image 20260602002334

修改后的代码如上图

pi += sum[id]这行代码会导致数据竞争,导致“更新丢失”,很多线程的 sum[id] 根本没被真正加进 pi 里,所以最终算出来的 π 值(3.1384…)比正确值小。

Synchronization

Pasted image 20260602003611

仅仅在C语言的层面上无法使用Lock解决数据竞争问题,比如下面这个例子:

Pasted image 20260602003701 Pasted image 20260602003724

(上图的代码y坐标表示时间,从上到下依次发生) 两个threads同时发现锁空闲,想要set lock,此时lock会被两个thread set

Pasted image 20260602004225

Hardware Synchronization

Pasted image 20260602005015 Pasted image 20260602005550

上述的amoadd指令的3个细分步骤,其实是一个原子指令,它拥有绝对的不可分割性

Pasted image 20260602010433

li t0, 1加载立即数:将1加载到register t0

在不可打断的一个 CPU 时钟周期内,硬件强行把 t0 里的 1 塞进内存的锁里,同时把内存里原本的值拔出来,放进了 t1 里。

这段代码的精妙之处在于它“先斩后奏”。它不管三七二十一,直接用原子操作把锁设为 1。判断自己是否抢锁成功的关键,全在那个被替换出来的旧值 t1 身上:

  • 情况 A(抢锁成功):如果被换出来的 t1 里面的值是 0。说明在这一瞬间之前,锁是空闲的。你成功把 0换成了 1。恭喜,你拿到了锁!
  • 情况 B(抢锁失败):如果被换出来的 t1 里面的值是 1。说明早有别的线程把锁占了。虽然你也霸道地往内存里写了个 1(把别人的 1 覆盖成了 1,状态没变),但通过检查换出来的旧值,你发现自己来晚了,获取锁失败。

上述critical action表述的就是对共享变量的读写

Pasted image 20260602012042 Pasted image 20260602012058

使用OpenMP创建lock来规避数据竞争,当然也有更简洁的语法:

Pasted image 20260602012138 Pasted image 20260602012200 Pasted image 20260602012925
  • 这个函数返回的并不是一个绝对的标准时间戳(比如 1970年1月1日至今的秒数),而是从过去某个“任意参考点”开始计算的秒数(类似于电脑开机到现在的时间)。
  • 用于测量墙钟时间

Shared Memory and Caches

Pasted image 20260602013227 Pasted image 20260602013321 Pasted image 20260602013334

SMP 架构选择让所有的处理器/核心共享同一个统一的物理内存地址空间

Pasted image 20260602013703 Pasted image 20260602013714

上面的两张图引入了缓存一致性问题,cpu1,cpu2都需要用到address为1000的值(此处为20),因此他们从memory中获取,在cache中copy了一份。然后cpu0修改了address为1000的值为40,那么现在cpu1,cpu2里cache的值就是错的了

Cache Coherency

为啥把这个当一级标题,因为重要!

Pasted image 20260602014734

Big Idea:

  • 读操作随便分享 (If only reading…):如果大家都只是读取数据(比如 P1 和 P2 都读地址 1000 的值 20),那绝对安全。多个核心可以同时拥有同一个地址的副本。
  • 写操作必须广而告之 (If a processor writes…):一旦某个核心(比如 P0)想要修改这个数据,它绝不能偷偷摸摸在自己的 Cache 里改。它必须通过底层的互连网络(总线)通知其他所有人:“喂!我要改地址 1000 的数据了!”

Snoop缓存

Pasted image 20260619220904
  • 架构师的工作: 共享内存 → 保持缓存值一致(coherent)

  • 想法: 当任何处理器缓存缺失(miss)或写入(write)时,通过互连网络通知其他处理器 如果只是读,那么许多处理器都可以有副本(copies) 如果处理器写入,则使任何其他副本无效(invalid)

  • 写入来自一个处理器的事务,其他缓存“snoopy”公共互连检查它们持有的标签 使在其他缓存中具有相同地址且修改过的任何副本无效

Pasted image 20260619220953
  • Snoopy缓存标签是双端口的 (Dual-ported)” 这是这张PPT在硬件实现上强调的重点。为了让监听机制高效运作,缓存的标签和状态记录组(Tags and State)必须配备两个访问端口。

这两个端口的分工如下:

  • 左侧端口(面向 Processor): 这是常规的缓存工作端口。处理器(Processor)通过这里发送地址(A)、读写控制信号(R/W)和数据(D),进行日常的指令和数据存取。
  • 右侧端口(面向共享总线): PPT中标记为 Snoopy read port attached to Memory Bus。这是一个专门用于监听的只读端口。它连接到共享内存总线上,实时接收其他处理器发出的地址(A)和读写信号(R/W)。
  • 上方输出(作为总线主控): PPT中标记为 Used to drive Memory Bus when Cache is Bus Master。当当前的处理器遇到缓存未命中(Cache Miss),或者需要将修改后的数据写回主存时,这个缓存控制器就会接管总线(成为 Bus Master),向外发送地址和读写请求。此时,其他处理器的“右侧端口”就会监听到这个请求。
Pasted image 20260619221354

readmiss这里的意思:

  • Read miss (读缺失):CPU 需要读取某个数据,但在自己的缓存(Cache)里没找到。
  • Dirty copy (脏副本):在多核系统中,另一个 CPU 的缓存里有这个数据,并且它被修改过了。这意味着,那个 CPU 缓存里的数据是全系统最新的,而此时主存(内存)里的数据是没来得及更新的旧数据
  • Write back (回写):把缓存里最新修改过的数据,同步写回到主存里。

MSI

Pasted image 20260619221637
  • 工作机制: 当一个 CPU 要读数据时,数据被加载并标记为 S (共享)。如果要写数据,必须先通知所有其他拥有该数据的 CPU,让它们把状态变成 I (无效),然后自己才能把状态变成 M (修改) 并写入。
  • 致命痛点(为什么需要演进): 假设 CPU A 读取了一个数据(状态为 S),并且只有 CPU A 读取了它。紧接着,CPU A 想要修改这个数据。在 MSI 协议下,即使只有 A 拥有这份数据,它从 S 变成 M 的过程中,也必须向总线发一次广播(Invalidate 信号)。这种无意义的广播严重浪费了总线带宽
Pasted image 20260619222933

MESI

Pasted image 20260619223033

为了解决 MSI 中“单机读写还要发广播”的痛点,引入了 E (Exclusive, 独占) 状态。

  • 工作机制: 当 CPU A 读取一个数据,如果系统发现只有 CPU A 读取了,就会把它标记为 E (独占),而不是 S。
  • 解决的痛点: 此时如果 CPU A 想要修改这个数据,因为它是 E 状态,CPU A 知道绝对没有别人在用它,所以它可以悄悄地把状态从 E 变成 M,直接写入,不需要在总线上发任何广播。这极大地减少了串行程序在多核环境下的总线压力。

MOESI缓存一致性协议:

对缓存的内存访问是: Modified (in cache) 修改 Owned (in cache) 拥有 Exclusive (in cache) 独占 Shared (in cache) 共享 Invalid (not in cache) 作废

Pasted image 20260619221523 Pasted image 20260602015253 Pasted image 20260602015305 Pasted image 20260602020335

兼容性矩阵: 我们可以把这个矩阵看作是多核 Cache 之间的“关系图谱”。假设我们探讨的是同一个内存地址(比如地址 1000),行代表当前核心(Core A)的状态,列代表其他任意核心(Core B)的状态。勾叉代表是否能够同时出现。

Pasted image 20260602020046 Pasted image 20260619224723 Pasted image 20260619225055

伪共享

伪共享 (False Sharing):缓存一致性协议追踪和作废数据的最小单位,不是单个变量,而是一整个“缓存块 (Cache Block / Cache Line)”。

Pasted image 20260602020920

如上图,明明cpu0和cpu1用的是不同的地址,但是他们在一个缓存块内,其中一个cpu改了,另一个cpu就会任务自己cache中的那个缓存块作废了,因此需要经常访问内存并且总线通信

  • P0 写入 X:P0 修改了变量 X。根据我们上一节学的缓存一致性协议,P0 必须在总线上大喊:“我改数据了,你们手里的这个 32 字节的块全部作废!”
  • P1 躺枪:P1 的 Cache 收到了作废信号。虽然 P1 根本不关心 X,它只关心 Y,但因为 Y 跟 X 坐在同一条“32字节的船”上,P1 手里包含 Y 的整个缓存块被无情地标记为 Invalid(作废)
  • P1 写入 Y:轮到 P1 要修改 Y 了。它一查 Cache,发现数据作废了(Cache Miss)!P1 被迫去总线上重新请求最新的块。拿到块后,P1 修改了 Y,并大喊:“我改数据了,你们的块作废!”
  • P0 躺枪:P0 手里包含 X 的块又被作废了。
  • 当 P0 再次想要修改 X 时,它又得去总线上要数据,然后再把 P1 踢下线……
Pasted image 20260602021303

避免方法:减少缓存块大小

Pasted image 20260602021330通信失效 (Communication misses):真共享和伪共享都是

Pasted image 20260630003551

这里如果sun[0],sum[1]在同一个cache block/line里,就会产生伪共享 因此要让相邻两个线程操作的sum数组元素要在不同的block,选yellow:Constant for size of blocks in doubles - 块大小所包含的 double 数量

目录缓存

Pasted image 20260629223540

这一页说的是当处理器核心数目迅速增加之后,由于任何cpu cache miss 时,必须探测每一个其他缓存 当处理器核心数量增加(比如从 4 核增加到 64 核),这种“大喊大叫”的机制会遇到两个致命瓶颈:

  • 总线通信带宽 (Bus Bandwidth): 总线是一条公共通道。如果 64 个核都在频繁地广播请求,这条公共通道很快就会拥堵不堪。
  • 标签带宽 (Tag Bandwidth): 这是经常被忽略的一点。其他核心接收到广播后,必须去查询自己的 Cache Tag(标签阵列)来判断自己是否拥有该数据。如果每秒收到海量的监听请求,Cache Tag 就会被这些查询操作占满,导致该核心自己正常的读写操作被阻塞。

Idea:统计表明,当你向全网广播“谁有数据 X?”时,绝大多数核心的回答都是“我没有”。这意味着耗费了巨量带宽和 Tag 阵列资源的广播,99% 都是无效操作。既然大多数情况都找不到,那么“按需精确点对点通信”而不是“无脑全量广播”才是解决之道。

Pasted image 20260629231206 Pasted image 20260629231327
  • 内存行中这个状态字段会记录比如:当前到底有哪几个 CPU 核心把这块数据读进了自己的 Cache 里,以及它们是只读状态(Shared)还是已修改状态(Modified)
  • Cache miss的流程变为:step1. 找内存中负责该地址的 Directory 控制器; step2: Directory 查了一下自己的表格,看看哪些核心有这个数据; step3 : 不广播,只招特定的几个核心,使用点对点通信
  • 网络事务 (点对点通信): 在大规模众核处理器(如 64 核、128 核服务器 CPU)中,核心之间是通过类似互联网的 Mesh 网络(网格网络)连接的。查找目录、索要数据,都变成了网络中带有源地址和目的地址的数据包(Packets)。这就彻底摆脱了传统共享总线的物理限制。
Pasted image 20260629231750

所以,必须创造一个 Pending(瞬态)来充当“过渡锁”。 当 CPU A 发出请求的那一刻,它立刻给自己挂上 Pending 状态。这个状态的作用是:

  1. 防自己: 告诉自己的处理器核心:“数据在路上了,你先暂停(Stall),别读老数据。”
  2. 防别人(处理并发): 如果这段时间有别的网络包(比如别人的失效请求)找上门来,CPU A 看到自己是 Pending,就知道遇到了“并发撞车”。它不会盲目回复,而是会根据协议规则,把别人的请求缓存起来,或者让对方稍后重试(NACK)。
Pasted image 20260629232256

“dir是一组节点”意思就是,dir里面是一组cpu核心编号 TR、TW两个瞬态分别是Transient Read -> Write(从R(dir)到W(id)之间的状态),和Transient Write -> Read/Write(从W(id)到R(dir)之间的状态),触发场景如下

  • TR(dir) - 等待作废确认 (Transient Read -> Write):

  • 场景: 数据本来是好几个节点在共享读取 R(dir),突然节点 A 说:“我要独占这块数据并修改它!”

  • 动作: 主目录为了保证一致性,必须向原来在读取的那些节点(dir 集合)发送“作废(Invalidate)”请求。

  • 状态含义: 在所有的作废确认回复(ACK)收齐之前,主目录进入 TR(dir) 状态。意思是:“我正在等这帮老读者把手里的书撕掉,等他们全回复我了,我再把独占权交给 A。”

  • TW(id) - 等待数据写回 (Transient Write -> Read/Write):

  • 场景: 数据本来被节点 B 独占修改了 W(B)。这时节点 C 跑来跟主目录说:“我想读这块数据。”

  • 动作: 主目录自己手里的数据是过期的,它必须给节点 B 发消息:“B,赶紧把你改好的最新数据交出来(写回/Writeback)!”

  • 状态含义: 在节点 B 把最新数据传回来之前,主目录进入 TW(id) 状态。意思是:“我正在等那个独占了数据的家伙把最新版本还给我,拿到手之后我才能转发给 C。”

总结与对比snoop缓存和目录缓存

Pasted image 20260629233640 Pasted image 20260629234816
  • 一个例子展示了read miss中,目录缓存是怎么工作的。
  • 注意这里到达DRAM之后,发现时R状态,那么memory中的数据是新鲜可用的,就直接拿memory中的数据就行了,不需要向其他 CPU 索要数据。
Pasted image 20260629235211 Pasted image 20260630000431

类似于cache line的结构,但是Memory Line 是“物理上松耦合(甚至分离)”的: 虽然我们在画图时,会把 Directory 信息画在 Memory 旁边,好像它们是一个整体结构。但在真实的硬件主板上,负责存储目录信息的 SRAM 控制器,和负责存储真实数据的普通 DRAM 内存条,往往是分离的(或者存在内存的不同区域)。它们只是通过相同的“内存物理地址”在逻辑上被关联起来。

Pasted image 20260630001434

这里的序列化,本质就是“排队法则”。 因为在没有总线的分布式网络里,消息是满天飞且不按先后顺序到达的。为了保证大家看到的最终结果是一致的,无论在 Cache 端还是 Directory 端,都必须利用“瞬态 (Pending states)”充当交通信号灯,强行把那些因为网络原因超车、乱序到达的请求按在原地等待,迫使整个系统一步一个脚印地“按顺序(序列化)”处理事务。

强调瞬态的重要性

通常 L1 Cache Line、L2 Cache Line 以及 Memory Line 的大小都是完全一致的(目前业界绝对的主流标准是 64 字节)

Pasted image 20260630001837
  • 这里就是算出来缓存总共只有2M行之后,只在内存里用2M个memory line的状态位和共享向量来维护活跃行。
    • 引入 Tag: 因为现在账本只有 200 万行了,没法跟 256GB 内存的物理地址一一对应了。所以每个账本条目必须加贴一个 8 字节的 Tag(标签),写明“我这行记录的是内存里哪个地址的状态”。
  • 新的单行开销: 现在的目录项变成了 8字节(Tag) + 16字节(位向量) = 24字节。单行开销变大了(24/64=37.5%24 / 64 = 37.5\%)。
  • 最后ppt算错了,应该是:2097152×24=50,331,6482097152 \times 24 = 50,331,648 字节,等于 4915249152 KB,也就是 4848 MB

内存一致性模型 (Memory Consistency Model)

加速和缩放的类型(Types of Speedups and Scaling)

A. 问题的限制 (Problem Constrained) -> 强扩展 (Strong Scaling) / Amdahl 定律

  • 核心思想: 总工作量(问题规模)是固定不变的。
  • 目标: 疯狂加机器,只为了把这个固定的任务完成得越快越好(缩短执行时间)。
  • 增加处理器数量和内存大小
SPC=Time(1 processor)Time(p processors)S_{PC} = \frac{Time(1\text{ processor})}{Time(p\text{ processors})}

B. 时间的限制 (Time Constrained) -> 弱扩展 (Weak Scaling) / Gustafson 定律

  • 核心思想: 我们能容忍的等待时间是固定不变的。
  • 目标: 随着机器的增加,我们不强求把旧问题算得更快,而是把问题规模同比例放大,在相同的时间内做更多、更复杂的事情。
  • 目标是增加问题规模
  • 增加了处理器数量和内存大小
STC=Work(p processors)Work(1 processor)S_{TC} = \frac{Work(p\text{ processors})}{Work(1\text{ processor})}

生产者与消费者

Pasted image 20260630011013 Pasted image 20260630011028

在现代高性能硬件上,这段代码是错误的(或者说是不安全的)

  • 寄存器 (Registers) - 存“值”的地方:
  • xflag: 寄存器,存储 flag(0 或 1)。
  • xdata: 寄存器,存储 data(你需要传输的具体数据)。
  • 指针/地址 (Pointers) - 存“内存位置”的地方:
  • xflagp: 寄存器,存储 flag 变量在内存中的地址
  • xdatap: 寄存器,存储 data 变量在内存中的地址生产者 (Producer)
  1. sw xdata, (xdatap): 把我们要传的数据存入共享内存的 data 位置。
  2. li xflag, 1: 把 1 这个数字存进 xflag 寄存器。
  3. sw xflag, (xflagp): 把 1 写入共享内存的 flag 位置。
  • 逻辑: 我先写数据,写完后再把 Flag 置为 1,告诉消费者“数据准备好了”。

消费者 (Consumer)

  1. spin: lw xflag, (xflagp): 从共享内存的 flag 地址读出值,存入 xflag 寄存器。
  2. beqz xflag, spin: 检查 xflag 是否为 0。如果是 0,说明生产者还没写完,跳转回 spin 处继续读。如果不是 0(即读到了 1),继续往下执行。
  3. lw xdata, (xdatap): 既然 Flag 变 1 了,说明数据好了,从内存读取 data
Pasted image 20260630150151
核心维度Cache Coherence (缓存一致性)Memory Consistency (内存一致性模型)
作用范围单个内存位置(局部)多个不同内存位置(全局)
解决的痛点所有的处理器对同一个数据的修改顺序达成共识。所有的处理器对所有数据的读写操作顺序达成共识。
底层实现/代表Snoopy (总线监听), Directory (目录协议)SC (顺序一致性), TSO, 弱一致性 (Relaxed)

顺序一致性 (Sequential Consistency, 简称 SC)

Pasted image 20260630150849

Lamport 的原话非常严谨,我们可以把它翻译并拆解为两条必须同时满足的铁律:

  • 铁律一(全局串行化): “the result of any execution is the same as if the operations of all the processors were executed in some sequential order” 不管这些处理器在物理上是怎么并行的,它们最终的执行结果,必须看起来像是所有操作都被排成了一个全局的单步队列,大家排队一个接一个地执行。
  • 铁律二(局部不乱序): “and the operations of each individual processor appear in the order specified by its program” 在这个全局队列中,如果我们只挑出某一个特定处理器(比如 P1)的操作来看,P1 操作的先后顺序,必须和程序员在代码里写的一模一样。绝对不允许硬件擅自把 P1 的第二行代码提到第一行代码前面去执行。
Pasted image 20260630151324

大多数真正的机器都不是SC

存储缓冲区优化与TSO

Pasted image 20260630151749

Store Buffer 是一把双刃剑。它通过“异址重排”极大地提升了 CPU 性能,但也彻底粉碎了顺序一致性(SC)的美好幻想。正是因为它的存在,程序员才被迫发明了“内存屏障(Memory Fence)”去强行清空这个 Buffer,以保证多线程同步的正确性。

TSO(完全存储排序)模型的核心特征,就在 PPT 最上面那句话:允许按处理器对存储区进行本地缓冲 (Store Buffer)。

Pasted image 20260630152631
  • 上图属于写后读 (Store-Load) 乱序。 这个命名规则是以“程序员写代码的顺序(Program Order)”为基准的,而不是以硬件偷偷改变后的实际执行顺序为准。上图是先sw再lw,所以是写后读
  • TSO只允许写后读
Pasted image 20260630154436
  • 强模型就是类似于SC,一致性强,对程序员极其友好。你写的代码是什么顺序,它就是什么顺序。;
  • 弱模型就是顺序一致性弱,硬件段各种重排乱序,芯片可以设计得更简单、功耗更低、极限性能更高。代价是软件端写代码很难
Pasted image 20260630155149

Course index · Previous: Virtual Memory

Backlinks