Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 系统与高性能 / Systems & Performance

计算系统 / Computing Systems

1. 计算系统:计算机如何执行与加速程序

2. 从 C++ 源码到 CPU 执行

3. CPU 流水线、乱序执行与分支预测

4. Cache、一致性、伪共享与 NUMA

5. GPU、SM、Warp 与显存

6. 计算执行模型:程序怎样映射到机器

7. SIMD 与编译器向量化

8. C++ 多线程与 OpenMP

9. CUDA 平台与编程模型

10. CUDA Kernel、内存与性能

11. CPU-GPU 异构流水线

12. MPI 与分布式并行

13. 并行算法模式

14. 性能模型与工具

15. 递进学习项目:从单线程到集群

历史完整正文 / Original Deep Dives

1. 历史完整正文:统一前文章逐篇保留

原体系结构与硬件 / Original Architecture

1. 硬件编程与高性能计算:一张可走通的学习地图 / A Practical Learning Map for Hardware Programming and HPC

2. 计算机体系结构:CPU、内存与 GPU / Computer Architecture: CPUs, Memory, and GPUs

3. 计算机架构基础——为什么 GPU 比 CPU 更快 / Computer Architecture Fundamentals: Why GPUs Outperform CPUs

4. 并行计算理论——30 天训练能优化到多快? / Parallel Computing Theory and the Limits of Training Acceleration

5. GPU 架构深入——上万个核心如何分工协作 / GPU Architecture and Massive Parallel Execution

6. CUDA 编程模型——把矩阵乘法映射到 GPU / The CUDA Programming Model for Mapping Matrix Multiplication to GPUs

7. CUDA 内存管理——百亿参数如何装进显存 / CUDA Memory Management for Large Models

8. CUDA 性能优化——从 30 天缩短到 10 天 / CUDA Performance Optimization

9. CPU 并行编程——OpenMP 与 SIMD 向量化 / CPU Parallel Programming with OpenMP and SIMD

10. HPC 集群与 MPI——多节点分布式训练 / HPC Clusters and MPI for Distributed Training

11. 异构计算——CPU 与 GPU 如何协同工作 / Heterogeneous Computing with CPUs and GPUs

12. 深度学习训练优化实战——从 30 天到 3 天 / Deep Learning Training Optimization from Thirty Days to Three

13. 性能分析工具——找到真正的瓶颈 / Performance Analysis Tools for Finding Real Bottlenecks

14. NPU 全景——昇腾/寒武纪/TPU/苹果生态 / The NPU Landscape: Ascend, Cambricon, TPU, and Apple

15. 未来趋势——2030 年的计算机会是什么形态 / Future Computing Trends Toward 2030

16. 硬件与高性能计算:从“程序为什么慢”开始 / Hardware and HPC Starting from Why Programs Are Slow

原并行计算 / Original Parallel Computing

1. 并行计算:从 SIMD 到 MPI / Parallel Computing from SIMD to MPI

2. 并行计算全景:从晶体管、CPU、GPU 到计算集群 / Parallel Computing from Transistors, CPUs, and GPUs to Clusters

3. 并行计算基础:任务分解、加速比与可扩展性 / Parallel Computing Fundamentals: Decomposition, Speedup, and Scalability

4. 处理器体系结构:从指令流水线到多核芯片 / Processor Architecture from Instruction Pipelines to Multicore Chips

5. CPU 并行:多线程、SIMD、Cache 一致性与 NUMA / CPU Parallelism with Threads, SIMD, Cache Coherence, and NUMA

6. 内存层次:Cache、带宽、局部性与一致性 / Memory Hierarchies, Bandwidth, Locality, and Coherence

7. GPU 体系结构:SIMT、Warp、SM 与吞吐优先设计 / GPU Architecture with SIMT, Warps, and Streaming Multiprocessors

8. CUDA 编程模型:Thread、Block、Grid 与内存协作 / CUDA Threads, Blocks, Grids, and Cooperative Memory Access

9. 并行算法模式:Map、Reduce、Scan、Stencil 与任务图 / Parallel Patterns: Map, Reduce, Scan, Stencil, and Task Graphs

10. 异构计算:CPU、GPU、NPU 如何协同工作 / Heterogeneous Computing with CPUs, GPUs, and NPUs

11. 分布式并行:MPI、集合通信、RDMA 与多机多卡 / Distributed Parallelism with MPI, Collective Communication, and RDMA

12. 性能工程:测量、Roofline、瓶颈定位与优化闭环 / Performance Engineering with Measurement, Roofline, and Bottleneck Analysis

13. 并行计算实战:AI、CAE、图像与科学计算 / Parallel Computing for AI, CAE, Imaging, and Scientific Computing

14. 并行计算实践路线:从单核优化到多机多卡 / A Parallel Computing Project Path from Single-Core to Multi-Node GPUs

本页目录

CPU 流水线、乱序执行与分支预测 ​

为什么不等一条指令完全结束 ​

若取指、译码、执行和访存各占一个阶段,完全串行会让多数部件等待。流水线让多条指令处于不同阶段:

text
cycle       1       2       3       4       5
inst A    Fetch   Decode  Execute Memory  Retire
inst B            Fetch   Decode  Execute Memory ...
inst C                    Fetch   Decode  Execute ...
1
2
3
4

延迟表示一条指令从开始到结果可用的时间;吞吐表示单位时间完成多少指令。流水线主要提高吞吐,不会让单条依赖链凭空没有延迟。

三类依赖 ​

text
A: r1 = load [p]
B: r2 = r1 + 1       RAW:B 真正依赖 A
C: r3 = x * y        与 A/B 独立,可提前执行
D: if (r2) goto L    控制依赖:下一条地址未知
1
2
3
4
  • 数据依赖使消费者必须等待生产者;
  • 内存别名可能让 CPU/编译器无法确认 load 与 store 是否冲突;
  • 分支使取指前端暂时不知道下一条路径。

超标量与乱序执行 ​

常见现代 CPU 每周期可以译码、发射和执行多条微操作。乱序核心在不改变单线程可观察结果的前提下,让独立工作越过正在等待的指令。

text
program order:  A(load miss) -> B(depends A) -> C(independent) -> D
issue order:    A(waiting)   -> C -> D -> B(after A ready)
retire order:   A -> B -> C -> D
1
2
3

典型组成包括:

  • register renaming:消除名字造成的假依赖;
  • reservation stations / scheduler:等待操作数并选择可执行工作;
  • execution ports:整数、浮点、load/store 等资源;
  • reorder buffer(ROB):按程序顺序退休并支持精确异常。

这些名称和组织来自常见微架构,不是 ISO C++ 或所有 ISA 强制的固定布局。

分支预测与推测执行 ​

前端预测分支方向和目标,沿预测路径继续取指。预测正确时隐藏控制等待;预测错误时丢弃错误路径的未退休结果并恢复正确状态。

text
predict taken -> execute future work -> verify branch
                    | correct: retire
                    + wrong: flush and restart
1
2
3

可观察的体系结构状态会恢复,但推测可能影响 Cache 等微架构状态,这也是 Spectre 类侧信道的背景。安全问题不能简单表述为“CPU 执行了错误代码”。

SMT 不是双倍核心 ​

同时多线程(如 Intel Hyper-Threading)让一个物理核心保存多个线程的体系结构状态,共享前端、执行端口和 Cache 等资源。它能填补一条线程的等待空隙,但两个硬件线程会竞争资源,因此收益取决于工作负载。

对代码的启示 ​

  • 缩短长依赖链;
  • 让数据连续,减少 Cache miss;
  • 避免不可预测且工作很小的分支;
  • 不为“无分支”写难以维护的技巧,先测量;
  • 编译器可能进行 if-conversion、展开和向量化,源码外观不等于机器执行。

深入原理与工程实践 ​

前面的内容负责建立统一心智模型;下面把同一主题继续拆到执行过程、代码、性能代价与工程判断。

<!-- migrated-deep-dive:start -->

完整迁入:原硬件学习地图与 CPU 执行主线 ​

硬件编程与高性能计算:一张可走通的学习地图 / A Practical Learning Map for Hardware Programming and HPC ​

📅 创建时间:2026-06-02 🏷️ 标签:#Hardware #HPC #CUDA #GPU #并行计算 📚 前置知识:C++ 基础(会写类、指针、STL 即可) 📚 相关知识:[[/04-ai/01-llm-engineering/09-transformer-training-computation]] [[/04-ai/01-llm-engineering/10-transformer-inference-computation]] [[/03-web/05-backend-engineering/08-concurrency]]


文档目标 ​

如果 CUDA、GPU、Cache、SIMD、MPI 像一堆互不相干的缩写,先记住:它们都在解决同一件事——让数据更快地完成计算。

程序的耗时,大致可以拆成三部分:

text
总时间 = 计算时间 + 搬运数据的时间 + 相互等待的时间
1

容量则是另一条硬约束:数据装不下,再快的计算单元也无法直接工作。整套笔记就是沿着计算、存储、通信、容量四条线,理解从单核 CPU 到 GPU 集群的性能问题。

详细的分层阅读方法和生活化类比见 目录首页。第一次阅读不要求吃透所有公式、参数和代码。

学完后你应该能回答 ​
  • 为什么同一段循环,顺序访问通常比随机访问快?
  • 为什么 GPU 核心很多,却不适合所有任务?
  • 为什么用了 8 张 GPU,训练通常不会正好快 8 倍?
  • 为什么优化前要先判断瓶颈在计算、访存还是等待?
  • 为什么模型参数看似能装进显存,训练时仍可能 OOM?

两个例子贯穿全部章节 ​

先用向量相加理解机制,再用大模型训练理解规模:

向量相加(建立直觉)             大模型训练(落到真实系统)
        │                                  │
        ├─ 如何拆成独立任务                ├─ 参数和激活能否装下
        ├─ 线程如何分工                    ├─ 多卡如何分工与同步
        ├─ 数据放在哪里                    ├─ CPU 能否及时供给数据
        └─ 时间耗在哪里                    └─ 如何测量真实瓶颈
1
2
3
4
5
6

文中的“单卡训练 175B 模型 30 天”是用于串联概念的假设场景,不是严谨的可运行基线。单卡首先会遇到容量约束;不同精度、优化器和并行策略也会显著改变时间。学习时要把它当作问题引子,而不是性能结论。

章节之间的因果关系 ​
text
程序为什么慢?
├─ 单个处理器如何工作?        → 01 CPU、Cache、内存层次
├─ 工作能拆开同时做吗?        → 02 并行上限与效率
├─ GPU 如何执行这些工作?      → 03 GPU 架构 → 04 CUDA 线程模型
├─ 数据如何及时送到计算单元?  → 05 内存 → 06 优化 → 11 测量工具
├─ 一台机器还不够怎么办?      → 07 CPU 并行 → 08 多机多卡 → 09 协同
└─ 怎样用于 AI 训练?          → 10 训练优化 → 12 NPU → 13 趋势
1
2
3
4
5
6
7

建议实际按 01 → 02 → 03 → 04 → 05 → 11 → 06 → 07 → 09 → 08 → 10 阅读。性能分析放在优化之前,是为了先找到问题再选手段。


技术栈总览 ​

┌─────────────────────────────────────────────────────────────────┐
│                   硬件编程技术栈                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  应用层                                                         │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐             │
│  │  PyTorch   │  │  TensorRT  │  │  DeepSpeed │             │
│  │  (框架层)  │  │  (推理优化) │  │  (分布式)  │             │
│  └────────────┘  └────────────┘  └────────────┘             │
│                                                                 │
│  编程层                                                         │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐             │
│  │  CUDA C++ │  │  OpenMP     │  │  MPI       │             │
│  │  (.cu)    │  │  (#pragma)  │  │  (集群通信)│             │
│  └────────────┘  └────────────┘  └────────────┘             │
│                                                                 │
│  硬件层                                                         │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐             │
│  │  GPU       │  │  CPU 多核   │  │  HPC 集群  │             │
│  │  (NVIDIA) │  │  (x86/ARM) │  │  (多节点)  │             │
│  └────────────┘  └────────────┘  └────────────┘             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

给 C++ 攻城狮的 CUDA 工具链入门 ​

本节专门为只学过 C++ 的你准备。如果你只会 g++ main.cpp -o app,下面的内容让你从零搞懂 CUDA 项目的编译流程。

什么是 nvcc? ​

nvcc 是 NVIDIA CUDA 编译器(NVIDIA CUDA Compiler)。它的作用类似 g++,但专门用于编译 CUDA 代码。

g++  main.cpp   -o app    # C++ 代码 → CPU 可执行文件
nvcc main.cu    -o app    # CUDA 代码 → GPU 可执行文件
1
2

nvcc 实际上是一套工具链的入口,它会:

  1. 分离代码中的 CPU 部分和 GPU 部分
  2. 用 g++ 编译 CPU 部分
  3. 用 ptxas(NVIDIA 的汇编器)编译 GPU 部分
  4. 用 gold(链接器)合并两者
nvcc 的安装 ​
### Windows:安装 CUDA Toolkit
### https://developer.nvidia.com/cuda-downloads
### 下载后一路 Next,默认安装到:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.x

### 安装完成后,命令行验证:
nvcc --version
### 输出类似:
### nvcc: NVIDIA (R) Cuda compiler driver
### Cuda compilation tools, release 12.4, V12.4.131
1
2
3
4
5
6
7
8
9
10
第一个 CUDA 项目的文件结构 ​
my_cuda_project/
├── main.cpp          ← C++ 代码:CPU 端(Host),负责调度和数据传输
├── vector_add.cu     ← CUDA 代码:GPU 端(Device),具体计算逻辑
├── vector_add.cuh    ← CUDA 头文件:kernel 函数声明
└── CMakeLists.txt    ← 编译配置(用 CMake 代替直接调用 nvcc)
1
2
3
4
5

为什么分成 .cpp 和 .cu? CUDA 代码混合了 CPU 代码和 GPU 代码,但:

  • .cpp 文件:纯 CPU 代码,用 g++ 编译
  • .cu 文件:GPU 代码,用 nvcc 编译
  • nvcc 能同时处理两者,但你也可以分开编译后链接
编译方式一:直接用 nvcc ​
bash
### 最简单的编译方式(一行命令搞定)
nvcc vector_add.cu main.cpp -o vector_add

### 指定 GPU 架构(可选,加了编译更精确)
nvcc vector_add.cu main.cpp -o vector_add \
    --generate-code arch=compute_80,code=sm_80

### 参数解释:
###   --generate-code arch=compute_80,code=sm_80
###   compute_80  = 虚拟架构(PTX 汇编级别)
###   sm_80       = 真实架构(A100 的 compute capability 是 8.0)
###   简单理解为:告诉 nvcc 你的 GPU 是什么型号
1
2
3
4
5
6
7
8
9
10
11
12
编译方式二:用 CMake(推荐) ​

实际项目中都用 CMake 管理大型项目:

cmake
### CMakeLists.txt
cmake_minimum_required(VERSION 3.18)
project(my_cuda_project LANGUAGES CXX CUDA)

### 查找 CUDA
find_package(CUDAToolkit REQUIRED)

### 你的源文件
add_executable(vector_add main.cpp vector_add.cu)

### 链接 CUDA 库
target_link_libraries(vector_add PRIVATE CUDA::cudart)
1
2
3
4
5
6
7
8
9
10
11
12
bash
mkdir build
cd build
cmake ..
cmake --build . --config Release
./vector_add
1
2
3
4
5
常用 nvcc 编译选项速查 ​
nvcc main.cu -o app [选项]

常用选项:
  -o <file>               输出文件名
  -arch=sm_XX             指定 GPU 架构(如 sm_80 = A100)
  -O0 / -O1 / -O2 / -O3  优化级别(默认 -O3)
  -g                       生成调试信息(gdb 可调)
  -lineinfo                生成行号信息(cuda-gdb 可调)
  --ptx                    只输出 PTX 汇编,不生成最终二进制
  --run                    编译后直接运行(仅限简单程序)
  -Xcompiler -Wall         把 g++ 的警告也显示出来

常见架构对照:
  sm_70  = V100
  sm_80  = A100
  sm_86  = RTX 3090 / A5000
  sm_89  = Orin
  sm_90  = H100 / H200
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
常见错误与排查 ​
### 错误1:nvcc: command not found
### 原因:CUDA 没有加入 PATH
### 解决:
###   Windows: 系统环境变量 → PATH → 添加
###   C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.x\bin

### 错误2:unsupported GPU architecture
### 原因:nvcc 版本太老,不支持你的 GPU
### 解决:升级 CUDA Toolkit,或用 -arch=sm_XX 指定更低架构

### 错误3:undefined reference to 'cudaMalloc'
### 原因:没有链接 CUDA 运行时库
### 解决:target_link_libraries(... CUDA::cudart)

### 错误4:kernel launch failed: no kernel image is available
### 原因:编译时指定的 GPU 架构和运行时的 GPU 不匹配
### 解决:重新用正确的 -arch=sm_XX 编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
开发工具推荐 ​
┌──────────────────────────────────────────────────────────────┐
│                    CUDA 开发工具推荐                          │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  IDE                                                          │
│  ✅ VS Code + NVIDIA Nsight VS Code Edition(免费)           │
│  ✅ Visual Studio + Nsight Visual Studio Edition(功能最强)  │
│  ✅ CLion(支持 CUDA 语法高亮)                               │
│                                                              │
│  命令行调试                                                   │
│  ✅ cuda-gdb          ← GPU 专用调试器                        │
│  ✅ compute-sanitizer ← 内存错误检测(越界/未初始化/竞争)     │
│                                                              │
│  性能分析                                                     │
│  ✅ NVIDIA Nsight Systems   ← 系统级 timeline 分析            │
│  ✅ NVIDIA Nsight Compute   ← GPU kernel 级别性能分析         │
│                                                              │
│  在线实验(无需本地 GPU)                                     │
│  ✅ Google Colab + 切换到 T4 GPU(免费,有时间限制)          │
│  ✅ Lambda Lab / Vast.ai(租用 GPU 云服务器)                 │
│                                                              │
└──────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C++ 到 CUDA C++:最小的认知跨越 ​

作为 C++ 程序员,你需要理解的核心变化只有一个:

┌────────────────────────────────────────────────────────────┐
│                                                            │
│  C++:代码在 CPU 上运行                                    │
│         ↓                                                  │
│  main() → 调用函数 → 函数在 CPU 上执行                     │
│                                                            │
│  CUDA C++:代码分为两部分                                   │
│                                                            │
│  Host(C++):负责调度、数据传输、控制流                    │
│  Device(CUDA):负责大规模并行计算                        │
│                                                            │
│  你写的 .cu 文件 = Host 代码 + Device 代码(混合在一起)    │
│                                                            │
│  最简单的理解:                                             │
│  CUDA = C++ + 一套新的关键字(__global__, __device__ 等)  │
│  + 一套新的运行时 API(cudaMalloc, cudaMemcpy 等)          │
│                                                            │
└────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// main.cpp(C++,Host 端)
int main() {
    // 申请 GPU 显存
    float* d_a;
    cudaMalloc(&d_a, N * sizeof(float));

    // CPU 数据拷到 GPU
    cudaMemcpy(d_a, h_a, N * sizeof(float), cudaMemcpyHostToDevice);

    // 调用 GPU kernel(launch 一个 grid)
    add<<<blocks, threads>>>(d_a, d_b, d_c);

    // GPU 结果拷回 CPU
    cudaMemcpy(h_c, d_c, N * sizeof(float), cudaMemcpyDeviceToHost);

    // 释放显存
    cudaFree(d_a);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// vector_add.cu(CUDA C++,Device 端)
// __global__ = 在 GPU 上运行,可从 CPU 调用
__global__
void add(float* a, float* b, float* c, int N) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        c[i] = a[i] + b[i];
    }
}
1
2
3
4
5
6
7
8
9

你不需要立刻理解上面每个细节——第 4 章会从头讲清楚。现在只需要记住:CUDA C++ 就是在 C++ 里加了 GPU 并行计算的能力。


硬件技术分层图谱 ​

┌─────────────────────────────────────────────────────────────┐
│                    硬件编程技术分层                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  分布式训练层                                               │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐          │
│  │  数据并行   │  │  模型并行   │  │  流水线并行  │          │
│  │ (DDP/DeepS)│  │  (Megatron)│  │  (PipeDream)│          │
│  └────────────┘  └────────────┘  └────────────┘          │
│                                                             │
│  通信层                                                     │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐          │
│  │  NCCL     │  │  NVLink    │  │  InfiniBand │          │
│  │  (GPU间通信)│  │  (卡间直连) │  │  (节点间)   │          │
│  └────────────┘  └────────────┘  └────────────┘          │
│                                                             │
│  编程框架层                                                 │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐          │
│  │  CUDA C++ │  │  OpenMP    │  │  MPI       │          │
│  │  (.cu)    │  │  (#pragma) │  │  (集群)    │          │
│  └────────────┘  └────────────┘  └────────────┘          │
│                                                             │
│  GPU 硬件层                                                 │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐          │
│  │  流式多处理器│  │  全局/共享内存│  │  TensorCore │          │
│  │  (SM)      │  │  (Global/  │  │  (矩阵加速) │          │
│  │            │  │   Shared)  │  │            │          │
│  └────────────┘  └────────────┘  └────────────┘          │
│                                                             │
│  CPU 硬件层                                                 │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐          │
│  │  多级缓存   │  │  SIMD 单元  │  │  NUMA 拓扑  │          │
│  │  L1/L2/L3  │  │  AVX-512   │  │  (多路)    │          │
│  └────────────┘  └────────────┘  └────────────┘          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

AI 辅助 vs 必须深入原理 ​

┌─────────────────────────────────────────────────────────────┐
│              AI 可查 vs 必须理解清单                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  可以高度依赖 AI(偏记忆/查表型):                         │
│  ✅ CUDA API 具体语法——AI 随时告诉你用什么函数              │
│  ✅ 特定 GPU 型号的参数——A100/H100 的 SM 数量/显存大小      │
│  ✅ CUDA .cu 文件的 kernel 模板——矩阵乘法代码框架           │
│  ✅ nvcc 编译选项——需要时查文档即可                        │
│  ✅ NPU 厂商的 SDK 命令速查——昇腾 CANN API 列表            │
│                                                             │
│  必须深入理解原理的(决定你能否解决实际问题的):           │
│  🔴 阿姆达尔定律——知道并行优化的理论上限在哪里              │
│  🔴 GPU 内存层次——为什么合并访问能提升 10 倍性能           │
│  🔴 SIMT 执行模型——Warp 分支分化如何影响性能               │
│  🔴 CUDA Stream 异步执行——如何让计算和通信重叠             │
│  🔴 显存 OOM 的根因——百亿参数为什么放不下单卡              │
│  🔴 NPU vs GPU 的生态差异——昇腾 CANN 和 CUDA 的本质区别    │
│                                                             │
│  原则:能查到的不用背,理解不了的面试必挂。                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

学习路径与面试频率 ​

┌─────────────────────────────────────────────────────────────┐
│              面试频率 × 理解深度 矩阵                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                    高面试频率                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  CUDA 线程层次(Thread/Block/Grid)    ⭐⭐⭐ 高理解  │   │
│  │  GPU vs CPU 架构差异                    ⭐⭐⭐ 高理解  │   │
│  │  CUDA 内存模型(Global/Shared/Register)⭐⭐⭐ 高理解  │   │
│  │  阿姆达尔定律 + 加速比                  ⭐⭐⭐ 高理解  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│                    中面试频率                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  SIMD 向量化(AVX-512/SVE)              ⭐⭐ 中理解   │   │
│  │  OpenMP 并行编程                       ⭐⭐ 中理解   │   │
│  │  MPI/NCCL 通信原语                      ⭐⭐ 中理解   │   │
│  │  混合精度训练(FP16/BF16)               ⭐⭐ 中理解   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│                    低面试频率(但要会用)                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  NPU 生态(昇腾/寒武纪/TPU)             ⭐  理解概念  │   │
│  │  性能分析工具(nsys/ncu)                 ⭐  会用即可  │   │
│  │  DPU/存算一体                             ⭐  理解概念  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

各章节详情 ​

章节主题核心场景
[[01-computer-architecture-basics]]计算机架构基础为什么 GPU 比 CPU 更适合训练任务
[[02-parallel-computing-theory]]并行计算理论阿姆达尔定律告诉你 30 天的优化极限
[[03-gpu-architecture]]GPU 架构深入GPU 上万个核心如何分工协作
[[04-cuda-programming-model]]CUDA 编程模型第一个 .cu 文件的完整编写流程
[[05-cuda-kernel-and-memory]]CUDA 内存管理百亿参数如何装进显存
[[06-cuda-optimization]]CUDA 性能优化优化后从 30 天缩短到 10 天
[[07-openmp-simd]]CPU 并行编程OpenMP + SIMD 向量化
[[08-mpi-cluster-hpc]]HPC 集群通信多卡多节点分布式训练
[[09-heterogeneous-computing]]异构计算CPU + GPU 如何协同
[[10-dl-training-optimization]]训练优化实战混合精度 + DeepSpeed ZeRO
[[11-performance-analysis-tools]]性能分析工具找到真正的瓶颈
[[12-npu-landscape]]NPU 全景昇腾/寒武纪/TPU/苹果生态
[[13-future-trends]]未来趋势DPU/光计算/量子

开始之前:为什么硬件编程这么难? ​

因为硬件编程不是学"一个语言",而是学一套系统——从 C++ 代码到 GPU 电路,中间经过了好几层抽象:

你写的 C++ 代码
      ↓(g++ 编译)
CPU 汇编 / 机器码 → CPU 执行

你写的 CUDA C++ 代码
      ↓(nvcc 编译)
分离 → Host 端用 g++ 编译 → CPU 控制
     → Device 端用 ptxas 编译 → GPU 并行执行
1
2
3
4
5
6
7
8

每一层的抽象都在"欺骗"你:

  • C++ 让你以为内存是无限的
  • CUDA 让你以为 GPU 线程是免费的
  • 分布式训练让你以为计算是线性加速的

学完这 13 章,你就能看穿这些"谎言",写出真正高效的代码。


学习状态:🟡 开始学习


完整迁入:原处理器体系结构:流水线到多核 ​

处理器体系结构:从指令流水线到多核芯片 / Processor Architecture from Instruction Pipelines to Multicore Chips ​

📅 创建时间:2026-07-20 🏷️ 标签:#CPU #体系结构 #流水线 #乱序执行 #多核 📚 前置知识:[[01-parallel-foundations]] 📚 相关知识:[[/02-systems-and-performance/02-computer-architecture-and-hardware/01-computer-architecture-basics]]


1. 一条指令怎样执行 ​

处理器执行指令通常可以抽象为:

text
取指 → 译码 → 读取操作数 → 执行 → 访存 → 写回
1

如果每条指令必须完整结束后才能开始下一条,大量硬件会处于空闲。因此处理器使用流水线,让多条指令处于不同阶段。

text
周期 1:指令 A 取指
周期 2:指令 A 译码,指令 B 取指
周期 3:指令 A 执行,指令 B 译码,指令 C 取指
1
2
3

流水线提高吞吐,但不会等比例降低单条指令的延迟。


2. 指令级并行 ​

现代 CPU 会在单个核心内部寻找互不依赖的指令并行执行。

超标量 ​

一个周期可以发射多条指令到不同执行单元,例如整数单元、浮点单元和加载存储单元。

乱序执行 ​
cpp
a = load(slow_address); // Cache Miss
b = x + y;              // 与 a 无关
c = m * n;              // 与 a 无关
1
2
3

CPU 不必等待第一条加载结束,可以先执行后两条指令,最后按程序顺序提交结果。

分支预测 ​

遇到 if 时,CPU 会预测执行路径并提前工作。预测错误后,错误路径的流水线结果必须丢弃。

这也是规则循环通常比大量不可预测分支更容易获得高性能的原因。


3. 从提高频率转向增加核心 ​

早期处理器主要依赖提高时钟频率,但功耗近似受到频率和电压的共同影响:

text
动态功耗 ≈ 电容 × 电压² × 频率
1

频率继续增长带来严重发热和能耗问题,行业转向:

  • 多核 CPU
  • 更宽的 SIMD
  • 专用加速器
  • 大规模 GPU

这意味着软件必须显式或隐式地利用并行,才能继续获得性能增长。


4. 核心、硬件线程与逻辑处理器 ​

物理核心 ​

拥有独立的执行资源,可以真正与其他核心并行。

同时多线程 SMT ​

一个物理核心维护多个硬件线程上下文。当一个线程等待数据时,另一个线程使用部分空闲执行资源。

text
8 核 16 线程 ≠ 16 个完整核心
1

SMT 的收益依赖工作负载。两个都占满相同执行单元的线程可能互相竞争。


5. CPU、GPU 与专用加速器 ​

特性CPUGPUNPU/TPU
核心数量少量复杂核心大量较简单执行单元大量矩阵/张量单元
控制能力强中等较弱或领域限定
单线程延迟低较高依任务而定
吞吐能力中等高特定算子极高
适合任务操作系统、业务逻辑、复杂分支图形、矩阵、规则数据并行AI 推理与训练

CPU 花费大量晶体管降低单线程延迟,GPU 把更多面积用于执行单元以提升总吞吐。


6. 并行层次 ​

一台现代计算机中同时存在多层并行:

text
多节点并行
└─ 单节点多处理器/多 GPU
   └─ 多核并行
      └─ SMT 硬件线程
         └─ SIMD 向量并行
            └─ 流水线与乱序执行
1
2
3
4
5
6

高性能程序通常不是只选择其中一种,而是组合多层并行。例如 MPI 跨节点、OpenMP 跨 CPU 核、SIMD 处理核心内部的数据。


7. 峰值性能不等于应用性能 ​

理论浮点峰值可以粗略表示为:

text
峰值 FLOPS = 核心数 × 频率 × 每周期浮点操作数
1

但应用还受到以下因素限制:

  • 数据是否在 Cache 中
  • 指令是否能够并行发射
  • 分支预测是否准确
  • SIMD 通道是否充分利用
  • 内存带宽是否足够
  • 多核之间是否频繁同步

因此硬件参数是上限,不是性能承诺。


核心总结 ​

  • 单核内部已经包含流水线、乱序执行和 SIMD 等并行机制。
  • 多核是功耗墙之后性能增长的主要方向。
  • CPU 追求低延迟和通用性,GPU 追求高吞吐。
  • 真正的并行程序往往同时利用多个体系结构层次。

下一篇:[[03-cpu-parallelism]] <!-- migrated-deep-dive:end -->

面试速答 ​

流水线、超标量和乱序执行有什么区别? ​

流水线让不同指令占据不同处理阶段;超标量允许同周期处理多条指令;乱序执行让已就绪的独立指令越过等待者,但通常按程序顺序退休以维持精确状态。

分支预测失败为什么昂贵? ​

错误路径上的在途工作要被清空,前端从正确地址重新填充;代价依赖流水线、预测器、工作集和具体微架构。

自测与答案 ​

  1. 流水线提高延迟还是吞吐? **答案:**主要提高稳态吞吐;单条指令延迟不必降低。
  2. 乱序执行是否改变单线程可观察语义? **答案:**正常实现必须保持 ISA 规定的可观察结果,通常借助按序退休。
  3. SMT 为什么不等于两个物理核心? **答案:**硬件线程共享同一核心的许多执行和缓存资源。

下一篇:Cache、一致性与 NUMA

最后更新于:

Pager
上一篇2. 从 C++ 源码到 CPU 执行
下一篇4. Cache、一致性、伪共享与 NUMA

持续记录,持续成长

Copyright © Tidenflow