# PyTorch 源码漫游

> 从一行 loss.backward() 出发，看清这台深度学习引擎底层到底怎么转。

- 作者：David（道雾轩）
- 最后更新：2026-08-03
- 原文：https://daiw.org/manual/pytorch-internals
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 序 · `loss.backward()` 之后，到底发生了什么

写 [nanoGPT](https://daiw.org/manual/nanogpt) 时，你敲下一行 `loss.backward()`，梯度就**神奇地**出现在每个参数的 `.grad` 里。你写 `a + b`，它在 CPU 上、GPU 上、要不要求导，全都「自动」处理妥当。你从没关心过这些——因为 PyTorch 把底下那台庞大的引擎，藏在了一层极薄的 Python 外衣底下。

这个专栏想掀开这层外衣，**顺着源码走一遍**，看清引擎到底怎么转：

> **PyTorch 是一台用 C++ 写的庞大引擎，披着一件轻薄的 Python 外套。** 你平时碰的是外套；这趟漫游，我们往下走，一层层看到引擎的齿轮。

## 为什么值得往下看

平时把 PyTorch 当黑箱用，完全没问题。但总有些时刻，你会希望自己看得更深一点：

- `loss.backward()` 报了个看不懂的 autograd 错误，栈里全是 `AccumulateGrad`、`grad_fn`——它们到底是什么？
- 想写个**自定义算子**或**自定义反向**，却不知道该从哪一层介入；
- `torch.compile` 有时飞快、有时「graph break」失效，想知道它到底在编译什么；
- 显存莫名其妙就满了，想搞懂 PyTorch 到底怎么管 GPU 内存。

这些问题的答案，都不在 API 文档里，而在**引擎的构造**里。看懂它，你调试、扩展、优化 PyTorch 时，就从「碰运气」变成「知道自己在动哪根齿轮」。

## 方法：一次调用，从 Python 走到金属

<Callout type="warn">
  **先说清楚这趟漫游的性质。** PyTorch 源码有**上百万行** C++/Python，且版本间变动频繁——不可能像 [nanoGPT 那样逐行读完](https://daiw.org/manual/nanogpt)。所以本专栏是一次**导览（漫游）**，不是某个 commit 的逐行注释：我们锁定引擎的**几个核心子系统**，讲清它们的**构造与原理**，给出**真实的源码位置**（文件 / 类 / 函数）供你按图索骥，并处处标注权威出处（Edward Yang 的 PyTorch internals 系列、官方文档、PyTorch 的论文）。具体某个文件路径、某个 dispatch key 的名字会随版本漂移，**以你本地 `import torch; torch.__version__` 那一版为准**——但底层的**设计思想**，多年来相当稳定。
</Callout>

漫游的主线，是**跟着一次调用往下走**：一句 `a + b`（或 `loss.backward()`）从 Python 出发，一层层穿过 PyTorch 的身体，直到落在 CPU/GPU 的金属上。走完这一趟，你就看清了整台引擎的骨架。

## 路线图

**第一站 · 数据结构** —— 一切的地基：`Tensor` 到底是什么，它和底层 `Storage` 是什么关系，`view`、`stride`、`contiguous` 这些词背后是同一个内存怎么被「不同地看」。

**第二站 · 一次调用的旅程** —— `a + b` 从 Python 怎么落到具体的 CPU/CUDA 内核。核心是 PyTorch 的中枢神经：**dispatcher（分发器）**，以及它下面的 **ATen 算子库**和 `native_functions.yaml`。

**第三站 · autograd 引擎** —— 最神奇的部分。前向时它怎么**悄悄记账**、把每步运算连成一张计算图；`loss.backward()` 又怎么**逆着回放**这张图、用链式法则把梯度算出来、堆进 `.grad`。

**第四站 · 上层与底层** —— 向上看 `nn.Module` 那层薄薄的糖（参数、buffer、hook 怎么组织）；向下看张量在 **GPU** 上怎么活（CUDA 流、异步、以及那个「一上来就吃光显存」的缓存分配器）。

**第五站 · 编译提速** —— `torch.compile` 怎么给一个「动态到骨子里」的框架做图编译：**TorchDynamo** 抓 Python 字节码、**TorchInductor** 生成飞快的内核。

**收尾** —— 把整个技术栈串成一张图，回望这趟从 Python 到金属的旅程。

## 读之前，你最好有

- **用过 PyTorch**（读过 [nanoGPT](https://daiw.org/manual/nanogpt) 就够）——你其实**已经用过**本专栏要拆的每一个东西（`Tensor`、`backward`、`nn.Module`、`.to('cuda')`、`torch.compile`），只是没往下看过。
- **会读 Python**；**C++ 能看个大概**更好，但看不懂 C++ 也没关系——涉及底层时我们讲思想、给位置，不要求你读 C++。
- 一颗「想知道齿轮怎么咬合」的好奇心。

准备好了，我们从最底下的地基开始：你天天用的 `Tensor`，其实只是一层「怎么看内存」的说明书。👉 [Tensor 与 Storage：同一块内存的不同看法](https://daiw.org/manual/pytorch-internals/tensor-and-storage)

## 本专栏篇目

- [PyTorch 源码漫游](https://daiw.org/manual/pytorch-internals.md): 从一行 loss.backward() 出发，看清这台深度学习引擎底层到底怎么转。

### 第一站 · 数据结构

- [Tensor 与 Storage · 同一块内存的不同看法](https://daiw.org/manual/pytorch-internals/tensor-and-storage.md): 你天天用的 Tensor，其实不是数据本身，而是一张「怎么读一块扁平内存」的说明书——sizes、strides、offset。看懂这个，view/transpose/切片为什么零拷贝、contiguous 是什么、view 和 reshape 差在哪，全都通了。

### 第二站 · 一次调用的旅程

- [一次 a + b 的旅程 · dispatcher 分发器](https://daiw.org/manual/pytorch-internals/dispatch-journey.md): 你写下 a + b，PyTorch 怎么从 Python 一路找到「该在 CPU 还是 CUDA、该不该记梯度、要不要混合精度」的正确内核？答案是它的中枢神经——dispatcher。而 autograd、autocast 其实只是这条分发链上的一层拦截。
- [算子与内核 · ATen 与 native_functions.yaml](https://daiw.org/manual/pytorch-internals/operators-and-kernels.md): dispatcher 把请求路由到内核，那内核本身写在哪？看 PyTorch 的张量库 ATen、那本记着两千多个算子的「花名册」native_functions.yaml，以及 TensorIterator 怎么让一份内核搞定广播、类型提升与 CPU/GPU 并行——外加 PyTorch 大量代码是「生成」出来的这件事。

### 第三站 · autograd 引擎

- [autograd（上） · 前向如何悄悄建图](https://daiw.org/manual/pytorch-internals/autograd-graph.md): 你只是正常地前向算，PyTorch 却在背后把每一步连成了一张「反向计算图」。看 requires_grad、grad_fn、saved tensors 各自是什么，以及这张动态图(define-by-run)是怎么在前向时一步步长出来的——这正是训练吃显存的原因。
- [autograd（下） · backward 如何逆向回放](https://daiw.org/manual/pytorch-internals/autograd-backward.md): loss.backward() 之后，引擎从 loss 出发、按拓扑逆序回放计算图：每个节点拿到上游传来的梯度，用链式法则（向量-雅可比积）算出对输入的梯度，再往上游传；多条路径的梯度相加，最后堆进叶子的 .grad。看清 seed=1、梯度累加、图被释放这些细节。

### 第四站 · 上层与底层

- [nn.Module · 参数、buffer 与 hook 的那层糖](https://daiw.org/manual/pytorch-internals/nn-module.md): nn.Module 自己不做任何数学，它只「组织」——用一个 __setattr__ 的小魔法，把你赋值的 Parameter / 子模块 / buffer 自动登记起来，于是 .parameters()、state_dict、.to(device) 全都水到渠成。看清它如何只靠「持有参数 + 定义 forward」，把求导整个甩给 autograd。
- [下到 GPU · CUDA 流与缓存分配器](https://daiw.org/manual/pytorch-internals/cuda-and-memory.md): 张量在 GPU 上怎么活？CUDA 调用是异步入队（所以慢 Python 能喂饱快 GPU，所以计时要 synchronize）；而那个让人困惑的「PyTorch 一上来就吃光显存」，真相是它自带的缓存分配器——为了绕开又慢又同步的 cudaMalloc，自己囤内存反复用。搞懂这两点，计时和 OOM 就不再玄学。

### 第五站 · 编译提速

- [torch.compile · 给动态图做编译](https://daiw.org/manual/pytorch-internals/torch-compile.md): eager 模式一个算子一个算子地即时执行，有 Python 开销、内核启动开销、还没法跨算子融合。torch.compile 怎么在不牺牲 Python 灵活性的前提下做图编译？看 TorchDynamo 抓字节码、装 guard、遇到搞不定的就「graph break」，以及 TorchInductor 把图编成 Triton/C++ 快内核。

### 收尾

- [收尾 · 从 Python 到金属，整台引擎的全景](https://daiw.org/manual/pytorch-internals/recap.md): 把整趟漫游拼成一张图——从 Tensor 说明书，到 dispatcher、ATen 内核、autograd 引擎、nn.Module、CUDA 分配器、torch.compile。追一行 loss.backward() 穿过所有层，回望这台「披着 Python 外衣的 C++ 引擎」，并把 nanoGPT / 大模型解构 / PyTorch 三部曲收成一个整体。
