📑 本页目录(点开跳转)
附录A · 报错反查与速查
⏱ 14 分钟 | ⭐ 撞墙的时候翻这一页:报错原文 → 它到底在说什么 → 去哪一章
🧭 怎么用这一页
这个板块不是 C++ 教程,它只回答一个问题:往下追 PyTorch 会撞到哪几堵墙。 所以这份速查也按「你撞到了什么」组织,而不是按语法点组织。
⚠️ 三张表的用法不同:报错反查表用来定位问题,判据速查用来做决定,术语对照用来读源码和读别人的报错。
🚨 一、报错反查:它到底在说什么
| 你看到的 | 它其实在说 | 去哪一章 |
|---|---|---|
undefined symbol: _ZN3c10... |
符号对不上。那串 _ZN3c10 是名字修饰(mangling)之后的 C++ 函数名,c10 是 PyTorch 的核心命名空间。你的扩展要的那个符号,在实际加载的库里不存在 |
04 · ABI 与二进制兼容 第四节 |
undefined symbol,但函数名看着明明有 |
⚠️ 两边的 _GLIBCXX_USE_CXX11_ABI 不一致。同一个函数在两种 ABI 下修饰出来的名字不一样,编译器认为它们是两个函数 |
04 第二、四节 |
cannot open shared object file / 库文件找不到 |
符号是对的,库本身没被找到。这是加载路径问题,不是编译问题 | 04 第五节 |
| 💀 什么都不报,但数据是错的 | 最贵的一种:结构体在两边的内存布局不一致,读写错位。它不会崩,只会给你错的数字 | 04 第六节 |
Could not run 'aten::gelu' with arguments from the 'SparseCPU' backend |
分派表里那一格是空的。算子名是行坐标,backend 是列坐标,这句话的意思是「这一行有,但这一列没实现」 | 05 · 算子怎么注册进 PyTorch 第一节 |
| 装 wheel 时提示平台不兼容 | wheel 标签和你的 glibc 版本对不上 —— manylinux 那套标签就是在描述「这个包能装在多老的系统上」 | 04 第七节 |
| 程序莫名其妙比单线程还慢 | ⚠️ 可能是 false sharing:两个逻辑上无关的变量落在同一条 cache line 上,两个核在互相打架 | 03 · cache 与数据布局 第六节 |
| 对象出了作用域之后再用就崩 | 悬垂(dangling)。这是这个板块第 02 章真正存在的理由 | 02 · 值语义与所有权 第七节 |
⭐ 一条通用判据:
undefined symbol是链接期的事,Could not run是运行期查表的事。 前者去查 ABI 和编译选项,后者去查分派表 —— 这两条路完全不通,别串。
🧮 二、判据速查:该怎么选
| 你在犹豫 | 判据 |
|---|---|
| 值传递还是引用传递 | ⚠️ C++ 的 = 默认是复制,和 Python 的「贴名字」完全相反。源码里到处是 const Tensor&,就是为了不复制 |
std::move 要不要用 |
记住它不移动任何东西 —— 它只是把对象标记成「可以被搬走」,真正搬不搬由接收方决定 |
| 用哪种指针 | 四种指针 = 四种所有权声明。选哪种等于在回答「谁负责释放」 |
| 资源怎么保证释放 | RAII:把释放绑在析构上。它是 C++ 版的 with |
| 数据怎么摆(AoS 还是 SoA) | 看你每次遍历用到几个字段。只用一两个字段就该拆成 SoA,否则每条 cache line 里大半是白搬的 |
| 结构体为什么变胖 | 对齐填充。字段按大小从大到小排,通常能省下来 |
| 要不要真去编译 PyTorch | ⭐ 多半不需要。06 · 读源码路线图 第五节给了只用 Python 就能查分派表的办法 |
🔍 三、不下载源码也能查的三件事
| 想知道 | 怎么查 |
|---|---|
| 某个算子的真实 schema | 用 PyTorch 自带的接口直接读,不必去翻 native_functions.yaml |
| 某个算子在哪些 backend 上有实现 | torch._C._dispatch_dump("aten::算子名") —— ⭐ 它把那一行的分派表直接打出来 |
| 报错到底来自哪一层 | 从 Could not run 里抽出算子名和 backend,再去 dump 那一行 |
⚠️
native_functions.yaml不在 pip 装的 wheel 里 —— 它是构建期的输入文件。 想看算子定义,用上面这两条命令比找源码快得多。
📖 四、术语对照
| 词 | 一句话 |
|---|---|
| 名字修饰 / mangling | 编译器把 C++ 函数名连同参数类型编码成一个唯一符号,_ZN3c10... 就是它的样子 |
| ABI | 二进制层面的约定:名字怎么编码、结构体怎么排布、异常怎么传。⚠️ 和 API 是两件事 |
| API | 源码层面的约定:函数长什么样、怎么调用。API 兼容不等于 ABI 兼容 |
| manylinux | 一套「能装在多老的 Linux 上」的 wheel 标签标准,本质是在钉死一条 ABI 基线 |
| cache line | CPU 一次从内存搬的最小单位。你取一个 int,它搬的是一整条 |
| false sharing | 两个无关变量落在同一条 cache line,多核互相使对方的缓存失效 |
| AoS / SoA | 结构体数组 / 数组的结构体。同一批数据的两种摆法,遍历模式决定该选哪个 |
| RAII | 资源获取即初始化:构造时拿、析构时还,异常路径也保证还 |
| 悬垂 / dangling | 对象已经没了,还有指针或引用指着它 |
| 分派表 / dispatch | PyTorch 用「算子名 × DispatchKey」两个坐标查实现的那张表 |
| DispatchKeySet | 列坐标的集合:CPU / CUDA / SparseCPU / Autograd… 决定这次调用落到哪一格 |
| TORCH_LIBRARY | 声明算子的宏。和注册实现的宏各管一件事 |
| pybind11 | 只需头文件的绑定库,把 C++ 函数包成 Python 能直接调的模块 |
🔗 这一页连到哪里
| 去哪 | 为什么 |
|---|---|
| 00 · 怎么用这份教程 | 六堵墙分别是什么、这个板块不讲什么、以及那些内容在站内哪里 |
| 01 · Python 调 C++ 的那条边界 | 跨语言调用的两笔开销:转换成本,和那把锁 |
| NumPy 与向量化思维 05 · 内存布局与 stride | ⭐ cache 那一章的上游:同一个「按行还是按列走」的问题,在 NumPy 里长什么样 |
| PyTorch 这个框架本身 09 · 自定义算子 | Python 侧怎么写自定义算子 —— 05 章讲的是它在 C++ 那一半 |