One line, several jobs一行代码,几种不同的工作
PyTorch is a tensor-computing framework with automatic differentiation. Its eager runtime turns tensor operations into concrete implementations; its optional compiler stack can capture and optimize larger regions of a program. Python is a familiar entrance, ATen supplies tensor operations, and lower-level libraries perform much of the arithmetic. Training also needs a record of how results depend on inputs.PyTorch 是支持自动求导的张量计算框架。它的 eager 运行时把张量操作交给具体实现;可选的编译栈则能捕获并优化更大范围的程序。Python 是熟悉的入口,ATen 提供张量运算,底层库完成大量数值计算。训练还需要记录结果怎样依赖输入。
y = torch.matmul(a, b)
Read this line as a request: “multiply these two tensors, using their shapes, storage layouts, devices, and differentiation state.” The tensor values are only part of the request. The framework must interpret the operation, choose implementations, allocate an output, perform the calculation, and—when needed—attach gradient history.可以把这一行理解为一张请求单:“根据这两个张量的形状、存储布局、设备和求导状态,完成乘法。”数值只是请求的一部分。框架还要解释运算、选择实现、分配输出、执行计算,并在需要时关联梯度历史。
By the end, you should be able to locate the 2-D branch, explain why the result has an MmBackward0 node, and separate a dispatch decision from the kernel that calculates numbers. Each later installment will zoom into one part of this journey.读完后,你应该能够定位二维分支,解释结果为什么带有 MmBackward0 节点,并区分“选哪个实现”和“实际算出数值”。后续每一期会放大研究这条路径中的一个部分。
Start with numbers you can check先用能够手算的数据
Use A with shape (2, 3) and B with shape (3, 2). Their inner dimensions agree, so Y has shape (2, 2). The product is a sum of row–column products; there is no elementwise broadcasting in this 2-D case.令 A 的形状为 (2, 3),B 为 (3, 2)。内侧维度一致,因此 Y 的形状为 (2, 2)。每个输出都是一行与一列的乘积之和;这个二维例子不是逐元素广播乘法。
import torch
assert torch.__version__.split("+")[0] == "2.10.0"
a = torch.tensor([[1., 2., 3.], [4., 5., 6.]], requires_grad=True)
b = torch.tensor([[7., 8.], [9., 10.], [11., 12.]], requires_grad=True)
y = torch.matmul(a, b)
print("version:", torch.__version__)
print("shape:", tuple(y.shape))
print("y:", y.tolist())
print("grad_fn:", type(y.grad_fn).__name__)
y.sum().backward()
print("a.grad:", a.grad.tolist())
print("b.grad:", b.grad.tolist())
torch.testing.assert_close(y, torch.tensor([[58., 64.], [139., 154.]]))
torch.testing.assert_close(a.grad, torch.tensor([[15., 19., 23.], [15., 19., 23.]]))
torch.testing.assert_close(b.grad, torch.tensor([[5., 5.], [7., 7.], [9., 9.]]))
Run this in a local Python environment with PyTorch installed. The article’s generic Godbolt Python runner does not supply this pinned PyTorch package, so this block is a local reproduction script. The independent C++ example later in the article can run in the embedded compiler.请在安装了 PyTorch 的本地 Python 环境中运行。站点通用 Godbolt Python 运行器不提供这里固定版本的 PyTorch 包,因此本代码块用于本地复现。文章后面的独立 C++ 示例可直接在站内编译器运行。
python -m pip install torch==2.10.0 --index-url https://download.pytorch.org/whl/cpu
Observed CPU outputCPU 实际输出
version: 2.10.0+cpu
shape: (2, 2)
y: [[58.0, 64.0], [139.0, 154.0]]
grad_fn: MmBackward0
a.grad: [[15.0, 19.0, 23.0], [15.0, 19.0, 23.0]]
b.grad: [[5.0, 5.0], [7.0, 7.0], [9.0, 9.0]]
Watch one output being assembled观察一个输出怎样累加出来
Each step highlights one scalar multiplication. Change A[0,0], then replay: the first row of Y changes. The arithmetic is real JavaScript computation, but the scalar ordering is an educational animation; optimized BLAS kernels use blocking, vectorization, and possibly multiple threads.每一步高亮一次标量乘法。修改 A[0,0] 后重放,Y 的第一行会改变。这里的算术由 JavaScript 实际计算,但标量顺序只是教学动画;优化后的 BLAS 内核会使用分块、向量化,也可能使用多线程。
At the original inputs, Y[0,0] = 1 × 7 + 2 × 9 + 3 × 11 = 58.初始输入下,Y[0,0] = 1 × 7 + 2 × 9 + 3 × 11 = 58。
Follow the request through the runtime跟随请求穿过运行时
Think of ATen as the menu of tensor operations, the dispatcher as the routing desk, and the backend as the workshop. Autograd adds a recipe for the return trip. These responsibilities interact: matmul can call another operator, and an autograd wrapper can redispatch the same operator below the autograd layer. The route is not one pass through a universal pipeline.把 ATen 想成张量运算菜单,dispatcher 想成分派工作的位置,backend 则是实际加工的车间。Autograd 为回程留下求导配方。这些职责相互协作:matmul 可以调用另一算子,求导包装层也能绕过自身重新调度同一算子。调用不是沿着一条通用流水线只走一遍。
Follow the arrows and click a node to inspect that operation. Forward computes values and records dependencies; backward sends gradients through those dependencies. The matrices are fixed to the example above.沿箭头追踪,点击图中节点查看对应操作。前向计算数值并记录依赖,反向沿这些依赖传递梯度。矩阵固定为上方示例。
Arrows: execution / gradients. Dashed: saved tensors. Highlight: current step. Scroll to inspect; steps follow automatically.箭头:执行/梯度流向;虚线:保存的张量;高亮:当前步骤。可滚动查看,逐步操作时自动跟随。
The source explanation below follows both values and gradients. Enable JavaScript to operate the diagram.下方源码解释同时介绍数值和梯度;启用 JavaScript 后可操作图形。
Read the source at five decision points在五个决策点阅读源码
1. Python binding: unpack a request1. Python 绑定:拆开请求
torch.matmul enters a generated C++ binding. The generator uses PythonArgParser to interpret arguments and chooses a dispatch expression for an overload. Ordinary tensor arguments cross this boundary as tensor handles; they are not converted into Python lists and multiplied by a Python loop. Override protocols such as __torch_function__ can intercept other cases, which this example excludes.torch.matmul 进入生成的 C++ 绑定。生成器使用 PythonArgParser 解析参数,并为重载选择调用表达式。普通张量以张量句柄跨过这层边界,不会先变成 Python 列表、再由 Python 循环做乘法。__torch_function__ 等覆盖协议可以拦截其他情况,本例不涉及这些扩展。
Binding generator: parser template and overload dispatch ↗绑定生成器:参数解析模板与重载分派 ↗
static PythonArgParser parser({
${signatures}
}, /*traceable=*/${traceable});
ParsedArgs<${max_args}> parsed_args;
auto _r = parser.parse(${self_}, args, kwargs, parsed_args);
${check_has_torch_function}
switch (_r.idx) {
${dispatch}
}
The ${...} placeholders above belong to PyTorch’s code generator. This is a literal template excerpt, not a runnable C++ program. Generated bindings may live under torch/csrc/autograd/generated/; searching only checked-in Python functions misses this boundary.上面的 ${...} 是 PyTorch 代码生成器的占位符。这是原始模板摘录,不是可运行的 C++ 程序。生成的绑定可能位于 torch/csrc/autograd/generated/;只在已提交的 Python 函数中搜索,会漏掉这层边界。
2. Operator schema: define the promise2. 算子 schema:定义接口承诺
native_functions.yaml: matmul registration ↗native_functions.yaml:matmul 的注册信息 ↗
- func: matmul(Tensor self, Tensor other) -> Tensor
variants: function, method
dispatch:
CompositeImplicitAutograd: matmul
NestedTensorCPU, NestedTensorHPU, NestedTensorCUDA: matmul_nested
The schema names the operator and its arguments. variants: function, method describes function and method APIs. CompositeImplicitAutograd means this implementation can be expressed using other differentiable operators, which supply the usual gradient behavior. It does not mean “run on the CPU,” nor does it mean autograd is absent. Specialized registrations, such as nested tensors, can take other paths.schema 指定算子及参数,variants: function, method 对应函数和方法两类 API。CompositeImplicitAutograd 表示这个实现可由其他可求导算子组成,通常借助这些算子提供梯度行为。它不表示“在 CPU 上运行”,也不表示没有自动求导。嵌套张量等专门注册可以走其他路径。
3. Shape decides which smaller operation to request3. 形状决定下一次请求哪个算子
_matmul_impl: dimension-dependent branches ↗_matmul_impl:按维度选择分支 ↗
if (dim_tensor1 == 1 && dim_tensor2 == 1) {
return has_out ? at::dot_out(out, tensor1, tensor2) : tensor1.dot(tensor2);
} else if (dim_tensor1 == 2 && dim_tensor2 == 1) {
return has_out ? at::mv_out(out, tensor1, tensor2) : tensor1.mv(tensor2);
} else if (dim_tensor1 == 1 && dim_tensor2 == 2) {
return has_out ? at::mm_out(out, tensor1.unsqueeze(0), tensor2).squeeze_(0)
: tensor1.unsqueeze(0).mm(tensor2).squeeze_(0);
} else if (dim_tensor1 == 2 && dim_tensor2 == 2) {
return has_out ? at::mm_out(out, tensor1, tensor2) : tensor1.mm(tensor2);
For our 2-D × 2-D inputs and no out=, the branch returns tensor1.mm(tensor2). That is a new operator call. A 1-D × 1-D product uses dot; 2-D × 1-D uses mv; 1-D × 2-D temporarily adds and removes a dimension. Higher-rank inputs require batching, broadcasting, and sometimes folding into mm; “every matmul becomes bmm” is not a reliable rule.本例为二维乘二维,且没有 out=,因此分支返回 tensor1.mm(tensor2),这又发起了一次算子调用。一维乘一维使用 dot,二维乘一维使用 mv,一维乘二维先补一维再去掉它。更高维输入涉及批次、广播,有时还能折叠成 mm;不能把“所有 matmul 都变成 bmm”当作规律。
4. Dispatcher: pick a handler, then possibly dispatch again4. 调度器:选择处理函数,也可能再次调度
The dispatcher combines information from tensor arguments with thread-local state into a dispatch-key set, selects a registered handler, and invokes it. Device/backend, layout, and features such as autograd participate; this is richer than if (is_cuda). The numeric dtype also matters inside backend implementations, but it is not accurate to describe every dtype as its own top-level dispatch key.调度器结合张量参数与线程局部状态形成调度键集合,再查找并调用已注册的处理函数。设备/后端、布局以及 autograd 等功能都会参与,比一个 if (is_cuda) 丰富得多。数值 dtype 也会影响后端内部实现,但不能说每一种 dtype 都是独立的顶层调度键。
Dispatcher::call: extract keys and look up a kernel ↗Dispatcher::call:提取调度键并查找实现 ↗
auto dispatchKeySet =
op.operatorDef_->op.dispatchKeyExtractor()
.template getDispatchKeySetUnboxed<Args...>(args...);
#ifndef PYTORCH_DISABLE_PER_OP_PROFILING
For ordinary grad-enabled CPU inputs, mm has an autograd wrapper. It determines whether a backward node is needed, redispatches below autograd to obtain the numerical result, then associates the history and saved information. The backward engine runs later when the user requests backward. A handler called a “kernel” in a dispatch table may therefore be a wrapper, not the final numerical loop.对于开启梯度记录的普通 CPU 输入,mm 有 autograd 包装层。它判断是否需要反向节点,绕过求导层重新调度以取得数值结果,再关联历史和保存的信息。反向引擎是在用户随后请求 backward 时才运行。因此调度表里称为“kernel”的处理函数也可能是包装层,并非最终的数值循环。
mm delegates to structured mm.out backend implementations ↗mm 委托给结构化 mm.out 后端实现 ↗
- func: mm.out(Tensor self, Tensor mat2, *, Tensor(a!) out) -> Tensor(a!)
structured: True
dispatch:
CPU: mm_out_cpu
CUDA: mm_out_cuda
The functional mm entry uses structured_delegate: mm.out. Generated wrappers handle output setup and invoke the structured implementation. This does not mean our Python call used an explicit out= argument or that a profiler must show a separate public aten::mm.out event.函数式 mm 条目使用 structured_delegate: mm.out。生成的包装代码负责输出准备并调用结构化实现。这不意味着 Python 调用显式传入了 out=,也不意味着 profiler 必须出现独立的公开 aten::mm.out 事件。
5. Backend: arrange memory, then do the arithmetic5. 后端:安排内存,再完成数值计算
CPU mm implementation: reuse the addmm machinery ↗CPU mm 实现:复用 addmm 路径 ↗
TORCH_IMPL_FUNC(mm_out_cpu)(const Tensor & self, const Tensor & mat2, const Tensor & result) {
{
at::NoNamesGuard guard;
addmm_impl_cpu_(const_cast<Tensor&>(result), result, self, mat2, 0, 1);
}
}
The CPU implementation calls addmm_impl_cpu_ with beta = 0 and alpha = 1. In the GEMM contract, that selects a plain matrix product. The helper checks layouts and strides and prepares the arguments for the numerical implementation. It is not Python iterating over tensor elements.CPU 实现以 beta = 0、alpha = 1 调用 addmm_impl_cpu_。在 GEMM 的接口约定中,这恰好表示普通矩阵乘法。辅助函数检查布局与步幅,为数值实现准备参数;不是 Python 在遍历张量元素。
For float32 CPU GEMM, CPUBlas.cpp contains a BLAS path and a fallback stub, with additional build-dependent optimized branches. The linked BLAS library, CPU, shape, and build configuration determine the precise numerical routine. On CUDA, mm_out_cuda reaches CUDA BLAS machinery; the library and selected algorithm launch GPU work. Neither branch promises one fixed kernel for every input.float32 CPU GEMM 的 CPUBlas.cpp 中既有 BLAS 路径,也有回退 stub,以及取决于构建配置的优化分支。具体数值例程取决于链接的 BLAS 库、CPU、形状与构建配置。在 CUDA 上,mm_out_cuda 进入 CUDA BLAS 路径,由库及其所选算法启动 GPU 工作。两条分支都不保证所有输入使用同一个固定内核。
CPUBlas: float GEMM, BLAS selection and fallback ↗CPUBlas:float GEMM、BLAS 选择与回退 ↗
CUDA mm: the CUDA BLAS entry ↗CUDA mm:CUDA BLAS 入口 ↗
PyTorch 2.10: asynchronous execution ↗PyTorch 2.10:异步执行 ↗
Autograd keeps a recipe, not another copy of the Python programAutograd 留下的是求导配方
For these inputs, the observed MmBackward0 node is evidence of the mm computation used by the composite matmul implementation. The node type is an internal implementation detail, not a stable public API guarantee. With gradient recording enabled, the graph connects the output to the operations and leaves that produced it, saving the information needed for derivatives.本例实际出现的 MmBackward0 节点,对应组合式 matmul 使用的 mm 运算。节点类型属于内部实现细节,不是稳定的公开 API 承诺。启用梯度记录时,计算图把输出连接到生成它的运算与叶子张量,并保存求导所需的信息。
These formulas are for real-valued matrices. For L = y.sum(), G is a 2 × 2 matrix of ones. Each row of A.grad is therefore [15, 19, 23], while B.grad has rows [5, 5], [7, 7], and [9, 9]. This is why backward() itself can issue more matrix multiplications. Complex-valued derivatives require conjugation conventions beyond this example.这些公式针对实数矩阵。对于 L = y.sum(),G 是一个全 1 的 2 × 2 矩阵。因此 A.grad 每一行都是 [15, 19, 23],B.grad 的三行分别是 [5, 5]、[7, 7] 和 [9, 9]。这也解释了为什么 backward() 自身会发起更多矩阵乘法。复数求导还涉及共轭约定,不在本例范围内。
Derivative definitions for mm ↗mm 的导数定义 ↗
- name: mm(Tensor self, Tensor mat2) -> Tensor
self: mm_mat1_backward(grad, mat2, self.sym_sizes(), self.sym_strides(), self.layout(), 1)
mat2: mm_mat2_backward(grad, self, mat2.sym_sizes(), mat2.sym_strides(), mat2.layout(), 1)
result: at::mm(self_t, mat2_p) + at::mm(self_p, mat2_t)
The YAML defines rules used to generate autograd code; it is not interpreted afresh for every multiplication. Saved tensors, graph nodes, and version checks make differentiation possible, but can consume memory. torch.no_grad() suppresses new gradient history even if the inputs require gradients. Leaf gradients accumulate across backward calls unless cleared.这些 YAML 规则用于生成 autograd 代码,不会在每次乘法时重新解释。保存的张量、图节点与版本检查支撑自动求导,也会消耗内存。即使输入要求梯度,torch.no_grad() 也会禁止记录新的梯度历史。叶子梯度会在多次 backward 之间累加,除非主动清空。
PyTorch 2.10: autograd mechanics and grad modes ↗PyTorch 2.10:Autograd 机制与梯度模式 ↗
The backward diagram follows the engine’s dependency scheduling, then ends at AccumulateGrad. These leaf nodes update .grad; they do not perform an optimizer step. Switching the diagram to mean() divides the upstream gradient by the four elements of Y, so both leaf gradients become one quarter of the sum case.反向图沿着引擎的依赖调度推进,最终到达 AccumulateGrad。这些叶子节点更新 .grad,不会执行优化器更新。把图中的归约切为 mean() 时,上游梯度除以 Y 的元素数 4,两份叶子梯度因此都变成求和情形的四分之一。
Collect evidence on your own machine在自己的机器上寻找证据
A CPU profiler can confirm operator nesting; a dispatch table can show available registrations. Neither by itself proves which low-level BLAS microkernel ran. First measure a small, controlled eager example, then change one condition at a time.CPU profiler 可以确认算子嵌套,调度表可以查看可用注册。但单凭任何一个,都不能证明执行了哪一个底层 BLAS 微内核。先观察受控的小型 eager 示例,再一次改变一个条件。
import torch
from torch.profiler import profile, ProfilerActivity
a = torch.tensor([[1., 2., 3.], [4., 5., 6.]], requires_grad=True)
b = torch.tensor([[7., 8.], [9., 10.], [11., 12.]], requires_grad=True)
with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as trace:
y = torch.matmul(a, b)
for event in trace.events():
if event.name in ("aten::matmul", "aten::mm"):
print(event.name, event.input_shapes)
with torch.no_grad():
detached_result = torch.matmul(a, b)
print("no_grad:", detached_result.requires_grad, detached_result.grad_fn)
for name in ("aten::matmul", "aten::mm"):
print(name)
for line in torch._C._dispatch_dump_table(name).splitlines():
if line.startswith(("CPU:", "AutogradCPU:")):
print(line)
assert detached_result.grad_fn is None
assert {"aten::matmul", "aten::mm"} <= {event.name for event in trace.events()}
aten::matmul [[2, 3], [3, 2]]
aten::mm [[2, 3], [3, 2]]
no_grad: False None
The three lines above are the stable portion observed in this pinned CPU run. The table additionally reported composite “math kernel” entries for matmul at CPU and AutogradCPU, and a CPU kernel plus an autograd kernel for mm. Generated filenames and line numbers in that table describe the wheel’s build tree, not necessarily your checkout. torch._C._dispatch_dump_table is a private diagnostic API and may change.上面三行是本次固定版本 CPU 运行中可复现的部分。调度表还显示:matmul 在 CPU 和 AutogradCPU 上注册的是组合式 “math kernel”;mm 则分别有 CPU kernel 和 autograd kernel。表中的生成文件名与行号来自 wheel 的构建目录,不一定对应你的源码目录。torch._C._dispatch_dump_table 是私有诊断 API,后续版本可能变化。
Try three experiments: replace both matrices with 1-D vectors and look for aten::dot; change an inner dimension and read the shape error; compare grad-enabled and no_grad results. Do not infer a speedup from these tiny matrices or from profiler overhead.试做三个实验:把两个输入都换成一维向量,寻找 aten::dot;修改内侧维度,阅读形状错误;比较记录梯度与 no_grad 的结果。不要用这些小矩阵或 profiler 自身的开销推断性能提升。
Rebuild one small piece in C++用 C++ 重建一个小零件
This self-contained model has a tensor value, a CPU backend, and a lookup function. It makes the separation between public operation, backend selection, and arithmetic tangible. It deliberately supports only contiguous 2-D double data on CPU; it has no autograd, views, dtype dispatch, BLAS, or CUDA. It is original teaching code, not a transcription of PyTorch’s dispatcher.这个独立模型包含张量值、CPU 后端与查找函数,让“公开运算、后端选择、实际计算”的分工可以亲手运行。它仅支持 CPU 上连续存储的二维 double 数据,不实现 autograd、视图、dtype 分派、BLAS 或 CUDA。这是原创教学代码,不是 PyTorch 调度器的源码抄写。
#include <iostream>
#include <stdexcept>
#include <vector>
enum class Device { CPU, CUDA };
struct Tensor {
int rows, cols;
std::vector<double> data;
Device device = Device::CPU;
};
Tensor mm_cpu(const Tensor& a, const Tensor& b) {
if (a.rows < 0 || a.cols < 0 || b.rows < 0 || b.cols < 0 ||
a.cols != b.rows ||
a.data.size() != static_cast<std::size_t>(a.rows) * a.cols ||
b.data.size() != static_cast<std::size_t>(b.rows) * b.cols)
throw std::invalid_argument("invalid matrix shape or storage");
Tensor y{a.rows, b.cols,
std::vector<double>(static_cast<std::size_t>(a.rows) * b.cols, 0.0)};
for (int i = 0; i < a.rows; ++i)
for (int j = 0; j < b.cols; ++j)
for (int k = 0; k < a.cols; ++k)
y.data[static_cast<std::size_t>(i) * b.cols + j] +=
a.data[static_cast<std::size_t>(i) * a.cols + k] *
b.data[static_cast<std::size_t>(k) * b.cols + j];
return y;
}
using Kernel = Tensor (*)(const Tensor&, const Tensor&);
Kernel lookup(Device device) {
if (device == Device::CPU) return mm_cpu;
throw std::runtime_error("CUDA backend is not registered in this model");
}
Tensor matmul(const Tensor& a, const Tensor& b) {
if (a.device != b.device) throw std::invalid_argument("device mismatch");
return lookup(a.device)(a, b);
}
int main() {
Tensor a{2, 3, {1, 2, 3, 4, 5, 6}};
Tensor b{3, 2, {7, 8, 9, 10, 11, 12}};
const Tensor y = matmul(a, b);
for (int i = 0; i < y.rows; ++i) {
for (int j = 0; j < y.cols; ++j)
std::cout << y.data[static_cast<std::size_t>(i) * y.cols + j]
<< (j + 1 == y.cols ? '\n' : ' ');
}
return y.data == std::vector<double>{58, 64, 139, 154} ? 0 : 1;
}
Change the inputs and rerun. Mark both inputs as CUDA and observe the explicit “backend not registered” error; that is a capability boundary, not an invitation to pretend CPU computation happened on a GPU. Replacing the triple loop with a BLAS call should not require changing the public matmul interface.修改输入后重新运行。把两个输入都标为 CUDA,会得到明确的“后端未注册”错误;这表示能力边界,不能把 CPU 计算伪装成 GPU 执行。把三重循环替换成 BLAS 调用时,公开 matmul 接口应当无需改变。
What we can now explain—and where to go next现在能解释什么,下一步读哪里
The original line is a request to a layered runtime: the binding interprets it; the matmul composite chooses mm for this shape; dispatch and autograd wrappers coordinate implementation selection and history; a backend produces the numbers. Later, backward follows derivative rules. The exact path changes with shape, layout, device, modes, and build.原来的那一行向分层运行时发出请求:绑定解释参数;matmul 的组合实现为本例选择 mm;调度器与 autograd 包装层协调实现选择和历史记录;后端算出数值。之后 backward 才沿导数规则计算。具体路径会随形状、布局、设备、模式与构建配置变化。
torch.compile introduces another route: capture, transformation, and compilation can replace parts of eager execution with compiled regions. It is not an unavoidable stage inside every eager matmul call, and compilation does not imply every operation becomes one custom fused kernel. We will study that route after tensor storage, dispatch, and autograd.torch.compile 引入另一条路线:捕获、变换与编译可以把部分 eager 执行替换为编译区域。它不是每次 eager matmul 必经的阶段,也不意味着每个运算都会变成一个自定义融合内核。我们会在张量存储、调度和 autograd 之后再研究这条路线。
PyTorch 2.10: torch.compiler overview ↗PyTorch 2.10:torch.compiler 概览 ↗
The roadmap above is planned, not a list of published links. Next question: when b = a.T changes shape and strides, which object owns the actual bytes? Published installments are collected automatically in the series index.以上是后续规划,不是已发布文章链接。下一期先回答:b = a.T 改变形状和步幅时,究竟哪个对象拥有实际字节?已发布文章会自动汇总到专题目录。
Sources and reproduction notes来源与复现说明
All implementation links above are pinned to the same commit. Excerpts retain upstream names; omitted surroundings are visible at each linked location. Source review and CPU numerical/profiler checks support the main route. The browser animation models that route; it does not run PyTorch or inspect the visitor’s GPU.上面的实现链接全部固定到同一提交。摘录保留上游命名,未展示的上下文可在各链接处阅读。主线经过源码核对与 CPU 数值/profiler 检查;浏览器动画只是该路径的模型,不会运行 PyTorch 或探测访客 GPU。
API contract: torch.matmul in PyTorch 2.10 · Dispatcher walkthrough (living design notes)API 约定:PyTorch 2.10 的 torch.matmul · 调度器设计导读(持续更新的设计笔记)
PyTorch source excerpt license and noticesPyTorch 源码摘录的许可证与声明
The excerpts are from PyTorch, licensed under its BSD-style license. The original copyright notices, redistribution conditions, and disclaimer are reproduced below. They remain in the original language.源码摘录来自 PyTorch,使用其 BSD 风格许可证。下方保留原始版权声明、再分发条件和免责声明;法律文本保持原文。
From PyTorch:
Copyright (c) 2016- Facebook, Inc (Adam Paszke)
Copyright (c) 2014- Facebook, Inc (Soumith Chintala)
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
Copyright (c) 2011-2013 NYU (Clement Farabet)
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
From Caffe2:
Copyright (c) 2016-present, Facebook Inc. All rights reserved.
All contributions by Facebook:
Copyright (c) 2016 Facebook Inc.
All contributions by Google:
Copyright (c) 2015 Google Inc.
All rights reserved.
All contributions by Yangqing Jia:
Copyright (c) 2015 Yangqing Jia
All rights reserved.
All contributions by Kakao Brain:
Copyright 2019-2020 Kakao Brain
All contributions by Cruise LLC:
Copyright (c) 2022 Cruise LLC.
All rights reserved.
All contributions by Tri Dao:
Copyright (c) 2024 Tri Dao.
All rights reserved.
All contributions by Arm:
Copyright (c) 2021, 2023-2025 Arm Limited and/or its affiliates
All contributions from Caffe:
Copyright(c) 2013, 2014, 2015, the respective contributors
All rights reserved.
All other contributions:
Copyright(c) 2015, 2016 the respective contributors
All rights reserved.
Caffe2 uses a copyright model similar to Caffe: each contributor holds
copyright over their contributions to Caffe2. The project versioning records
all such contribution and copyright details. If a contributor wants to further
mark their specific copyright on a particular contribution, they should
indicate their copyright solely in the commit message of the change when it is
committed.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America
and IDIAP Research Institute nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.