计算执行模型:程序怎样映射到机器
“并行”不是一种东西,而是发生在不同层次的工作重叠。先辨认层次,再选择工具。
one problem
├── one instruction stream
│ ├── ILP: several independent instructions overlap inside a CPU core
│ └── SIMD: one instruction operates on several data lanes
├── several threads: usually shared-memory CPU parallelism
├── many GPU threads: SIMT execution on warps and SMs
└── several processes: separate address spaces, messages, possibly many nodes1. 串行、并发、并行、异步
- 串行:前一项完成后才开始下一项。
- 并发:多个任务生命周期重叠,单核也能靠切换实现。
- 并行:多个任务在同一时刻由不同执行资源推进。
- 异步:发起操作后不必原地等待,描述控制流而非硬件数量。
异步不必然并行,并发也不必然多核。
2. 三种 CPU 并行层次
ILP:指令级并行
同一线程中的独立指令可由流水线、超标量和乱序执行重叠。主要由处理器和编译器完成。
SIMD:数据级并行
一条 AVX/NEON 等向量指令操作多个数据元素。适合循环中同构、独立的计算。
线程级并行
把任务分给多个软件线程,并由多个核心真正并行执行。工具包括 std::thread、线程池和 OpenMP。
三者可以同时存在:8 个 CPU 线程中的每个线程都可能乱序执行并使用 SIMD。
3. GPU SIMT
CUDA 用大量逻辑线程描述工作,硬件把它们组织成 Warp 并映射到 SM。SIMT 让每个线程拥有索引和寄存器状态,但同一 Warp 的控制流越一致,利用率通常越高。
4. 进程与分布式执行
线程通常共享地址空间;进程拥有独立地址空间。MPI 通过显式消息让多个进程合作,它们可以在同一机器,也可以跨节点。
node 0 memory <-- network --> node 1 memory
rank 0 rank 1
no ordinary shared pointer crosses this boundary5. 怎样选择
| 问题特征 | 首先考虑 |
|---|---|
| 小规模、强依赖、控制复杂 | 优化串行与算法 |
| 连续数组的相同操作 | 自动向量化 / SIMD |
| 单机共享数据的粗粒度任务 | 线程池 / OpenMP |
| 海量规则数据并行 | GPU / CUDA |
| 数据超过单机或需要多节点 | MPI / 分布式系统 |
并行化前先问:工作能否独立?数据在哪里?同步多少?粒度是否大到足以覆盖调度与通信成本?
深入原理与工程实践
前面的内容负责建立统一心智模型;下面把同一主题继续拆到执行过程、代码、性能代价与工程判断。
<!-- migrated-deep-dive:start -->
完整迁入:原并行计算理论全文
并行计算理论——30 天训练能优化到多快? / Parallel Computing Theory and the Limits of Training Acceleration
📅 创建时间:2026-06-02 🏷️ 标签:#HPC #并行计算 #阿姆达尔定律 #Flynn分类 #加速比 📚 前置知识:[[01-computer-architecture-basics]](计算机架构基础) 📚 相关知识:[[03-gpu-architecture]](GPU 架构) [[08-mpi-cluster-hpc]](多卡并行)
先抓住直觉
十个人一起搬家,不会必然快十倍:有些物品只能由一个人处理,大家还要分工、沟通和等待。并行计算研究的就是“能同时做多少”以及“协作成本有多大”。
- 必须理解:可并行部分、串行部分、加速比和并行效率。
- 用到再查:公式推导、Flynn 分类名称和具体并行框架。
- 读公式前先问:增加资源后,剩下的时间究竟花在串行工作还是通信上?
场景:你的 30 天训练,理论极限是多少?
┌─────────────────────────────────────────────────────────────┐
│ │
│ 当前:单卡 A100 训练 175B 参数模型 │
│ 预计时间:30 天 │
│ │
│ 你决定用 8 卡并行训练。 │
│ 8 倍快?那就是 30/8 = 3.75 天? │
│ │
│ 等等。 │
│ 同事用 8 卡,跑了 5 天,还是没跑完。 │
│ 理论加速比不是 8 倍吗?瓶颈在哪里? │
│ │
└─────────────────────────────────────────────────────────────┘这一章,我们用数学工具回答这个问题:并行化的理论极限在哪里?
第1节:串行 vs 并行——最基本的区别
串行计算
你一个人搬家:
第1步:把书装进箱子
第2步:把箱子搬上车
第3步:开车到新家
第4步:把箱子搬进屋
→ 一个人,顺序执行,每一步必须等上一步完成并行计算
8 个人一起搬家:
第1步:每个人负责不同的房间(同时装书)
第2步:每个人搬自己负责的房间(同时搬运)
→ 8 个人可以同时工作用时间衡量
串行时间 T(1) = t1 + t2 + t3 + t4
并行时间 T(N) = max(t1, t2, t3, t4) / N + 同步开销 + 通信开销第2节:阿姆达尔定律——并行加速的数学天花板
定律内容
┌─────────────────────────────────────────────────────────────┐
│ 阿姆达尔定律(Amdahl's Law) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 任意程序的加速比由它的串行部分决定。 │
│ │
│ Speedup(N) = 1 / (S + P/N) │
│ │
│ 其中: │
│ S = 程序的串行部分占比(Serial fraction) │
│ P = 程序的并行部分占比(Parallel fraction) │
│ N = 并行 worker 数量 │
│ S + P = 1 │
│ │
└─────────────────────────────────────────────────────────────┘推到过程
程序的执行时间:
T(1) = S × T(1) + P × T(1) // 串行部分 + 并行部分(单核)
T(N) = S × T(1) + (P × T(1)) / N // 串行不变,并行按 N 加速
加速比:
Speedup = T(1) / T(N)
= T(1) / [S × T(1) + (P × T(1))/N]
= 1 / [S + P/N]数值例子
假设:程序有 10% 是串行的(S = 0.1),90% 可并行(P = 0.9)
Speedup(1) = 1 / (0.1 + 0.9/1) = 1.00 (无加速)
Speedup(2) = 1 / (0.1 + 0.9/2) = 1.82 (不是 2!)
Speedup(4) = 1 / (0.1 + 0.9/4) = 3.08
Speedup(8) = 1 / (0.1 + 0.9/8) = 4.71
Speedup(16) = 1 / (0.1 + 0.9/16) = 6.40
Speedup(∞) = 1 / 0.1 = 10 (理论极限!)
关键发现:
即使你有无穷多个核心,这个程序最多只能快 10 倍。
因为那 10% 的串行部分是无法并行的!图解:加速比曲线
┌─────────────────────────────────────────────────────────────┐
│ 阿姆达尔定律:加速比随核心数的变化 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 加速比 │
│ ↑ │
│ 10 ┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─(理论极限 10x) │
│ │ ╭''''''''''\ │
│ 8 ┤─ ─ ─╱ \ │
│ │ ╱ \ │
│ 6 ┤─ ─╱ \ │
│ │ ╱ \ │
│ 4 ┤─ ╱ \ │
│ │ ╱ \ │
│ 2 ┤─╱ \ │
│ │ ╱ \ │
│ 1 ┼──────────────────────────────────────────────────→ │
│ 2 4 8 16 32 64 核心数 N │
│ │
│ 串行比例 S=0.1(10%)的加速比曲线 │
│ 曲线越来越平,接近 10 的理论极限 │
│ │
└─────────────────────────────────────────────────────────────┘不同串行比例的影响
┌─────────────────────────────────────────────────────────────┐
│ 不同串行比例下的理论加速比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 串行比例 │ 2核 │ 4核 │ 8核 │ 16核 │ 理论极限 │
│ ──────────┼────────┼────────┼────────┼────────┼─────────┤
│ S = 5% │ 1.90 │ 3.48 │ 5.93 │ 9.38 │ 20x │
│ S = 10% │ 1.82 │ 3.08 │ 4.71 │ 6.40 │ 10x │
│ S = 20% │ 1.67 │ 2.67 │ 3.48 │ 4.44 │ 5x │
│ S = 50% │ 1.33 │ 1.60 │ 1.78 │ 1.94 │ 2x │
│ S = 90% │ 1.11 │ 1.19 │ 1.25 │ 1.31 │ 1.1x │
│ │
│ 结论:串行比例越高,增加核心数的收益越小。 │
│ │
└─────────────────────────────────────────────────────────────┘第3节:回到训练场景——你的 30 天能优化到多久?
训练过程的时间分解
┌─────────────────────────────────────────────────────────────┐
│ 大模型训练时间分解(单卡 A100) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 100% = Forward + Backward + Optimizer + Communication │
│ │
│ Forward(矩阵运算,并行) ≈ 40% │
│ Backward(梯度计算,并行) ≈ 40% │
│ Optimizer(Adam 更新,串行) ≈ 10% ← 串行部分! │
│ Communication(梯度同步) ≈ 10% ← 通信开销! │
│ │
│ 实际串行比例 S ≈ 20%(考虑通信开销) │
│ │
└─────────────────────────────────────────────────────────────┘8 卡并行能快多少?
S = 0.2(20% 串行 + 通信)
P = 0.8(80% 可并行)
Speedup(8) = 1 / (0.2 + 0.8/8)
= 1 / (0.2 + 0.1)
= 1 / 0.3
= 3.33x
30 天 / 3.33 = 9 天
但实际上你的同事跑了 5 天!为什么比理论还快?
→ 因为通信优化做得好(NVLink),实际 S 更小
→ 或者用了更好的并行策略为什么实际可能比理论快?(Gustafson 定律)
阿姆达尔定律假设问题规模固定,但实际训练中:
- 数据集大小是固定的
- 模型大小是固定的
- 但绝对通信时间可能比理论假设的小
Gustafson 定律(弱扩展视角):
Scaled Speedup = N + S × (1 - N)
假设 S = 10%(通信和同步开销 10%)
8 卡加速比 = 8 + 0.1 × (1 - 8) = 8 - 0.7 = 7.3x(接近理想的 8x)
这说明:当问题规模随核心数增加时,加速比更接近线性。第4节:Flynn 分类——并行计算的四大门派
四种计算模型
┌─────────────────────────────────────────────────────────────┐
│ Flynn 分类法 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 指令 数据 │
│ ↓ ↓ │
│ ┌────────┬────────┐ │
│ │ 单条 │ 单条 │ → SISD │
│ │ 指令 │ 数据 │ (传统 CPU) │
│ ├────────┼────────┤ │
│ │ 单条 │ 多条 │ → SIMD │
│ │ 指令 │ 数据 │ (GPU/向量机) │
│ ├────────┼────────┤ │
│ │ 多条 │ 单条 │ → MISD │
│ │ 指令 │ 数据 │ (少用) │
│ ├────────┼────────┤ │
│ │ 多条 │ 多条 │ → MIMD │
│ │ 指令 │ 数据 │ (集群/分布式) │
│ └────────┴────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘每种类型的代表
┌─────────────────────────────────────────────────────────────┐
│ Flynn 分类详解 │
├─────────────────────────────────────────────────────────────┤
│ │
│ SISD = Single Instruction Single Data │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ CPU:一条指令处理一个数据 │ │
│ │ for (i=0; i<N; i++) sum += a[i]; │ │
│ │ → 串行执行,循环 N 次 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ SIMD = Single Instruction Multiple Data │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ GPU:一条指令同时处理 N 个数据(一个 Warp = 32 个线程)│ │
│ │ c = a + b (同时做 N 次加法) │ │
│ │ → 一次执行,N 个 ALU 同时运算 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ MISD = Multiple Instruction Single Data │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 很少见:同一数据被多个指令处理 │ │
│ │ 典型场景:飞机的多个传感器对同一数据进行冗余计算 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ MIMD = Multiple Instruction Multiple Data │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 多节点集群:每个节点独立执行不同指令处理不同数据 │ │
│ │ Node0: 处理 Batch0 Node1: 处理 Batch1 │ │
│ │ → 数据并行训练就是 MIMD 的典型应用 │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘与训练场景的对应
AI 训练的三层并行:
Layer 1(GPU 内部) → SIMD:GPU 一个 Warp 同时处理 32 个数据
Layer 2(单节点多卡)→ MIMD:每个 GPU 独立处理不同的数据批次
Layer 3(多节点) → MIMD:每个计算节点独立运行,通过 MPI 同步
三层叠加:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Node 0 Node 1 Node 2 │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ GPU 0 │ │ GPU 0 │ │ GPU 0 │ │
│ │ GPU 1 │ │ GPU 1 │ │ GPU 1 │ │
│ │ GPU 2 │ │ GPU 2 │ │ GPU 2 │ │
│ │ GPU 3 │ │ GPU 3 │ │ GPU 3 │ │
│ └────────┘ └────────┘ └────────┘ │
│ ↑ ↑ ↑ │
│ NVLink NVLink NVLink │
│ ↑ ↑ ↑ │
│ ┌────┴───────────────────┴───────────────────┴────┐ │
│ │ InfiniBand / RoCE 网络 │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ Layer 1:GPU Warp SIMD(单指令多数据) │
│ Layer 2:同节点多 GPU MIMD(通过 NVLink │
│ Layer 3:多节点 MIMD(通过 InfiniBand) │
│ │
└─────────────────────────────────────────────────────────────┘第5节:并行效率——8 卡真的用了 8 倍算力吗?
并行效率公式
并行效率 E(N) = 实际加速比 / 理论加速比 = Speedup(N) / N
理想情况:E(N) = 1(100%,8 卡快 8 倍)
实际情况:E(N) < 1(因为有通信、同步、负载不均等开销)常见效率陷阱
┌─────────────────────────────────────────────────────────────┐
│ 并行效率损失的原因 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 通信开销 │
│ 每个迭代结束后,所有 GPU 必须同步梯度 │
│ 梯度大小 = 模型参数 × 4 字节(FP32) │
│ 175B 参数 = 700 GB → 8 卡各传 700 GB? │
│ 实际上通过 AllReduce 优化为 8 份 × 1 份传输 │
│ │
│ 2. 负载不均衡 │
│ 不同层的计算量不同,某些 GPU 先完成等待其他 GPU │
│ │
│ 3. 资源争抢 │
│ 多卡共享 PCIe/NVLink 带宽 │
│ │
│ 4. 启动开销 │
│ GPU kernel 启动有固定开销(小任务可能不值得并行) │
│ │
└─────────────────────────────────────────────────────────────┘估算 8 卡训练的效率
实际场景:
- 梯度同步(AllReduce):通信时间 ≈ 计算时间的 10-20%
- 负载不均:5%
- 同步开销:5%
实际并行效率 ≈ 70-80%
8 卡加速比 ≈ 8 × 0.75 = 6x
30 天 / 6 = 5 天
这就解释了为什么你的同事 5 天跑完——实际效率约 75%!第6节:如何提高并行效率——三种并行策略
数据并行(Data Parallelism)
┌─────────────────────────────────────────────────────────────┐
│ 数据并行示意 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1份模型参数(复制到每张 GPU) │
│ │
│ GPU 0 ← 处理 Batch 0-999 │
│ GPU 1 ← 处理 Batch 1000-1999 │
│ GPU 2 ← 处理 Batch 2000-2999 │
│ GPU 3 ← 处理 Batch 3000-3999 │
│ ... │
│ │
│ 每轮迭代结束后:AllReduce 汇总所有 GPU 的梯度 │
│ │
│ 优点:简单,实现容易 │
│ 缺点:所有 GPU 必须持有完整模型(显存压力大) │
│ │
└─────────────────────────────────────────────────────────────┘模型并行(Model Parallelism)
┌─────────────────────────────────────────────────────────────┐
│ 模型并行示意 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 模型按层切分,每张 GPU 持有部分层 │
│ │
│ Input │
│ ↓ │
│ GPU 0: Layer 1-24(Embedding + Transformer 1-12) │
│ ↓ │
│ GPU 1: Layer 25-48(Transformer 13-24 + Output) │
│ ↓ │
│ Output │
│ │
│ 前向传播:GPU 0 → GPU 1(中间激活值传递) │
│ 反向传播:GPU 1 → GPU 0(梯度传回) │
│ │
│ 优点:突破单卡显存限制,支持超大模型 │
│ 缺点:跨 GPU 通信量大,效率低(跨节点更严重) │
│ │
└─────────────────────────────────────────────────────────────┘流水线并行(Pipeline Parallelism)
┌─────────────────────────────────────────────────────────────┐
│ 流水线并行示意 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 把模型并行分成多个阶段,形成流水线 │
│ │
│ Stage 0 Stage 1 Stage 2 Stage 3 │
│ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │GPU 0 │ │GPU 1 │ │GPU 2 │ │GPU 3 │ │
│ │L1-L12 │ │L13-L24│ │L25-L36│ │L37-L48│ │
│ └───────┘ └───────┘ └───────┘ └───────┘ │
│ ↓ ↓ ↓ ↓ │
│ F0/B3 F1/B2 F2/B1 F3/B0 │
│ │
│ F=Forward, B=Backward │
│ 同一时刻每张 GPU 做不同的事情(流水线) │
│ │
│ 优点:减少流水线气泡,提高 GPU 利用率 │
│ 缺点:需要仔细调度,否则气泡多 │
│ │
└─────────────────────────────────────────────────────────────┘三种并行策略对比
┌─────────────────────────────────────────────────────────────┐
│ 三种并行策略对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 策略 │ 通信模式 │ 显存占用 │ 通信量 │
│ ────────────┼────────────────┼────────────┼───────────── │
│ 数据并行 │ AllReduce(梯度)│ 全模型×N │ 中等 │
│ 模型并行 │ 点对点(激活值)│ 全数据×N │ 大(激活值) │
│ 流水线并行 │ 点对点(阶段间)│ 全数据×N │ 中等 │
│ │
│ 实际部署(千亿参数模型): │
│ → 8×A100(80GB)单卡能装 70B 参数(FP16) │
│ → 175B 需要模型并行 + 流水线并行 + 数据并行 │
│ │
└─────────────────────────────────────────────────────────────┘升华:30 天优化到 3 天,需要什么?
┌─────────────────────────────────────────────────────────────┐
│ 加速路径推演 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 10x 加速从哪里来? │
│ │
│ 因素 1:GPU 架构(vs CPU) → ~20x 加速 │
│ 因素 2:混合精度(FP32 → FP16) → ~2x 加速 │
│ 因素 3:8 卡数据并行 → ~6x 加速 │
│ 因素 4:梯度累积(模拟更大 batch) → ~1.5x 加速 │
│ 因素 5:Activation Checkpointing → ~1.5x 加速 │
│ 因素 6:通信优化(NVLink) → ~1.2x 加速 │
│ │
│ 综合效果:20 × 2 × 6 × 1.5 × 1.5 × 1.2 ≈ 648x? │
│ 实际因为阿姆达尔定律和通信开销,远低于理论乘积 │
│ 实际效果:单卡 30 天 → 多卡 3 天(~10x) │
│ │
│ 关键:不是某一个优化,而是多个优化叠加 │
│ │
└─────────────────────────────────────────────────────────────┘"AI 可查 vs 必须理解"清单
AI 可查:
✅ NVLink、InfiniBand 的具体带宽数值——官网查
✅ AllReduce 的具体实现算法——Ring AllReduce vs Tree AllReduce
✅ 各种并行框架的具体配置参数——DeepSpeed/Megatron 文档
必须理解:
🔴 阿姆达尔定律公式:Speedup = 1 / (S + P/N)
🔴 串行比例 S 决定理论加速上限
🔴 Flynn 分类:SISD/SIMD/MISD/MIMD
🔴 SIMD = GPU 的核心执行模型
🔴 数据并行 vs 模型并行 vs 流水线并行的 trade-off
🔴 并行效率 = 实际加速比 / N(为什么 E < 1)学习状态:🟡 开始学习
完整迁入:原并行基础:分解、加速比与扩展性
并行计算基础:任务分解、加速比与可扩展性 / Parallel Computing Fundamentals: Decomposition, Speedup, and Scalability
📅 创建时间:2026-07-20 🏷️ 标签:#并行计算 #Amdahl定律 #Gustafson定律 #性能模型 📚 前置知识:[[00-parallel-computing-overview]] 📚 相关知识:[[/02-systems-and-performance/02-computer-architecture-and-hardware/02-parallel-computing-theory]]
1. 并行之前先判断“能否拆分”
假设程序由以下步骤组成:
读取数据 → 预处理 → 核心计算 → 汇总结果 → 写入文件只有相互独立的部分能够同时执行。任务之间常见依赖包括:
- 数据依赖:后一步需要前一步的结果
- 控制依赖:是否执行由前一步判断决定
- 资源依赖:多个任务竞争同一文件、锁或设备
数据并行与任务并行
数据并行:同一种操作处理不同数据
例:多个线程分别处理图像的不同行
任务并行:不同任务同时处理同一流程的不同阶段
例:一个线程读取、一个线程解码、一个线程推理GPU 主要擅长数据并行,CPU 更容易同时承载数据并行和任务并行。
2. 衡量并行性能
加速比
S(p) = T(1) / T(p)如果单线程需要 100 秒,8 线程需要 20 秒,则加速比为 5,而不是 8。
并行效率
E(p) = S(p) / p上例的并行效率是 5 / 8 = 62.5%。剩余能力消耗在串行部分、同步、调度和负载不均上。
吞吐与延迟
- 延迟:完成单个任务需要多久
- 吞吐:单位时间能完成多少任务
GPU 往往提高吞吐,但单个小任务不一定比 CPU 延迟更低。
3. Amdahl 定律:固定问题规模的上限
如果程序中可并行比例为 P,使用 N 个处理器:
S(N) = 1 / ((1 - P) + P / N)当 95% 可以并行时,即使处理器无限多:
最大加速比 = 1 / (1 - 0.95) = 20这说明优化串行路径可能比继续增加核心更重要。
| 可并行比例 | 理论最大加速比 |
|---|---|
| 50% | 2 倍 |
| 90% | 10 倍 |
| 95% | 20 倍 |
| 99% | 100 倍 |
4. Gustafson 定律:问题规模也会增长
现实中获得更多计算资源后,通常不是只想更快完成原问题,而是希望处理更大的网格、更高分辨率或更大的模型。
S(N) = N - α(N - 1)其中 α 是串行部分比例。Gustafson 定律解释了为什么超级计算机仍然有价值:资源增加后,可以扩大并行工作规模。
5. 强扩展与弱扩展
强扩展
保持总问题规模不变,增加处理器数量。
固定 1 亿个网格单元:1 核 → 8 核 → 64 核处理器越多,每个处理器分到的工作越少,通信占比最终会上升。
弱扩展
每个处理器的工作量保持不变,处理器增加时同步扩大问题规模。
每核处理 100 万个网格单元:1 核 100 万 → 64 核 6400 万弱扩展更能反映大型科学计算系统的容量能力。
6. 粒度、调度与负载均衡
并行粒度
- 粗粒度:任务大、调度少,但可能不均衡
- 细粒度:任务均衡机会多,但调度和同步成本高
任务耗时只有几百纳秒时,放进线程池可能比直接执行更慢。
静态调度与动态调度
- 静态调度:提前分工,开销低,适合任务耗时相近
- 动态调度:运行时领取任务,均衡好,但有额外竞争
负载不均衡
线程 A:100 ms
线程 B:102 ms
线程 C:98 ms
线程 D:300 ms ← 所有人都要等它并行阶段耗时由最慢参与者决定。
7. 同步不是免费的
互斥锁、原子操作、Barrier 和消息通信都会带来等待。常见优化方向:
- 减少共享可变状态
- 批量处理,降低同步频率
- 使用线程局部数据,最后统一归并
- 让通信与计算重叠
- 通过无锁结构降低阻塞,但不要默认无锁一定更快
章节测试
- 为什么 16 核程序通常无法获得 16 倍加速?
- 图像逐像素处理更接近数据并行还是任务并行?
- 固定网格规模增加节点属于强扩展还是弱扩展?
- 为什么任务拆得过细也会降低性能?
参考答案
- 存在串行部分、同步、调度、访存和负载不均。
- 数据并行。
- 强扩展。
- 调度、通信和同步成本可能超过任务本身。
下一篇:[[02-processor-architecture]] <!-- migrated-deep-dive:end -->
面试速答
普通 C++ 编译会自动使用多核吗? 一般不会自动把任意串行算法变成多线程;编译器会做 ILP 调度和可能的自动向量化,多核通常需要显式线程或并行框架。
线程和核心是什么关系? 线程是软件执行流,核心是硬件资源。可运行线程由 OS 调度到逻辑 CPU,一个核心还可能提供多个硬件线程。
自测
asyncAPI 是否证明任务正在另一个核心执行?- OpenMP 和 AVX 为什么可以叠加?
- MPI 是否只能用于多机?
答案:否;它们分别使用线程级与数据级并行;否,MPI 进程也可位于同一台机器。