Back to Paddle

前向调用链路详解

.agents/skills/paddle-design-eager-graph/references/forward-call-chain.md

3.4.07.0 KB
Original Source

前向调用链路详解

本文档详细说明 Paddle 动态图模式下,从 Python API 调用到 PHI Kernel 执行的完整 5 层调用链路。以 paddle.add(x, y) 为例。

代码生成器总览

前向链路中大部分 C++ 代码由 Python 脚本自动生成:

生成的文件生成器脚本
paddle/fluid/pybind/ops_api.ccpaddle/fluid/pir/dialect/op_generator/ops_api_gen.py
paddle/fluid/pybind/eager_op_function.ccpaddle/fluid/eager/auto_code_generator/generator/python_c_gen.py
dygraph_functions.ccpaddle/fluid/eager/auto_code_generator/generator/eager_gen.py
paddle/phi/api/lib/api.ccpaddle/phi/api/generator/api_gen.py

层① ops_api.cc — Python-C 映射

文件paddle/fluid/pybind/ops_api.cc

入口函数签名

cpp
static PyObject *add(PyObject *self, PyObject *args, PyObject *kwargs)

核心职责

  1. 作为 Python 与 C++ 之间的桥梁,注册到 Python 模块的 method table 中
  2. 调用 GetTensorFromArgsPyObject* 中提取 paddle::Tensor
  3. 判断当前运行模式:静态图(PIR)还是动态图(Eager)
  4. 动态图模式下转发到层②的 eager_api_add

GetTensorFromArgs 的作用

  • 接收 PyObject* 参数和位置索引
  • 检查参数类型是否为 Tensor(支持 DenseTensorDistTensor 等)
  • 提取底层 C++ paddle::Tensor 引用并返回
  • 处理可选参数(paddle::optional<Tensor>)的情况

关键特征:该层代码由 ops_api_gen.py 根据算子 YAML 定义自动生成,开发者一般无需手动修改。

层② eager_op_function.cc — 动态图 C++ 接口

文件paddle/fluid/pybind/eager_op_function.cc

入口函数签名

cpp
static PyObject *eager_api_add(PyObject *self, PyObject *args, PyObject *kwargs)

执行流程

  1. 参数解析:从 args/kwargs 中解析出所有输入 Tensor 和 Attribute。使用 CastPyArg2xxx 系列函数进行类型转换。

  2. Dist Tensor 转换:若处于分布式模式,将普通 Tensor 转化为 DistTensor,附加分布式 placement 信息。

  3. 自定义前处理:部分算子有特殊的前处理逻辑(如参数校验、默认值填充)。

  4. GIL 释放与执行

    cpp
    {
      eager_gil_scoped_release guard;
      // 选择 backend,调用层③
      out = add_ad_func(x, y);
    }
    

    释放 Python GIL 后进入纯 C++ 执行路径,执行完毕后自动重新获取 GIL。

  5. Backend 选择:根据输入 Tensor 所在的设备(CPU/GPU/XPU 等)选择合适的后端。

  6. 返回结果:将 C++ paddle::Tensor 包装为 PyObject* 返回给 Python。

层③ dygraph_functions.cc — 自动微分函数

文件paddle/fluid/eager/api/generated/eager_generated/forwards/dygraph_functions.cc

入口函数签名

cpp
paddle::Tensor add_ad_func(const paddle::Tensor& x, const paddle::Tensor& y)

这是构建反向图的核心层,执行流程如下:

3.1 AMP 与 Type Promotion

  • 若启用 AMP(自动混合精度),将输入 Tensor 的 dtype 转换为目标精度(如 float16/bfloat16)
  • 若算子支持 Type Promotion,对不同 dtype 的输入进行类型提升

3.2 获取输入 AutogradMeta

cpp
auto p_autograd_x = egr::EagerUtils::nullable_autograd_meta(x);
auto p_autograd_y = egr::EagerUtils::nullable_autograd_meta(y);

3.3 判断是否需要构建反向图

cpp
bool trace_backward = egr::Controller::Instance().HasGrad();
bool require_any_grad = egr::EagerUtils::ComputeRequireGrad(
    trace_backward, p_autograd_x, p_autograd_y);

只有当全局梯度开启且至少有一个输入需要梯度时,才构建反向图。

3.4 调用层④算子库 API

cpp
auto api_result = paddle::experimental::add(x, y);

3.5 构建反向图(核心步骤)

require_any_grad 为 true,执行以下步骤:

1. 创建 GradNode
   auto node = std::shared_ptr<AddGradNode>(new AddGradNode(...));

2. SetTensorWrapper — 保存前向 Tensor 供反向使用
   node->SetTensorWrapperx(x);
   node->SetTensorWrappery(y);

3. SetGradOutMeta — 建立 GradNode 的出度连接
   // 将输入 Tensor 的 AutogradMeta 信息写入 GradNode 的 bwd_out_meta_
   node->SetGradOutMeta(x, 0);  // slot 0
   node->SetGradOutMeta(y, 1);  // slot 1

4. SetHistory — 将 GradNode 写入输出 Tensor 的 AutogradMeta
   // 建立 out -> GradNode 的连接
   egr::EagerUtils::SetHistory(&p_autograd_out, node);

5. SetGradInMeta — 设置 GradNode 的入度信息
   // 记录输出 Tensor 的 meta 信息,供反向执行时校验
   node->SetGradInMeta(api_result, 0);

反向图边的方向out.AutogradMeta.grad_node_ 指向当前 GradNodeGradNode.bwd_out_meta_[i].adj_edge_ 指向后继(输入方向的)GradNode

层④ api.cc — PHI 算子库接口

文件paddle/phi/api/lib/api.cc

入口函数签名

cpp
Tensor paddle::experimental::add(const Tensor& x, const Tensor& y)

执行流程

4.1 构造 KernelKey

cpp
auto kernel_key = ParseKernelKeyByInputArgs(x, y);
// kernel_key = {backend=GPU, layout=NCHW, dtype=float32}

KernelKey 的 32 位 Hash 结构

|---31-20 扩展---|---19-12 DataType---|---11-8 DataLayout---|---7-0 Backend---|

4.2 选择 Kernel

cpp
auto kernel_result = KernelFactory::Instance().SelectKernelOrThrowError(
    "add", kernel_key);

KernelFactory 内部维护两级 map:api_name -> {KernelKey -> Kernel}。若找不到精确匹配的 Layout,回退到 ALL_LAYOUT

4.3 PrepareData

将输入 Tensor 转换到目标 backend/dtype/layout:

  • DataType 转换(如 float64 -> float32)
  • DataLayout 转换(如 NHWC -> NCHW)
  • Place 搬运(如 CPU -> GPU)
  • Contiguous 化(非连续内存 -> 连续内存)

4.4 InferMeta

推导输出 Tensor 的 shape、dtype、layout 等元信息,分配输出内存。

4.5 Kernel 执行

cpp
using kernel_signature = void(*)(const Context&, const DenseTensor&,
                                  const DenseTensor&, DenseTensor*);
auto* kernel_fn = kernel_result.kernel.GetVariadicKernelFn<kernel_signature>();
(*kernel_fn)(*dev_ctx, *input_x, *input_y, kernel_out);

层⑤ PHI Kernel 执行

最终执行注册在 PHI 算子库中的具体 Kernel 函数。Kernel 按 {算子名, Backend, DataType, DataLayout} 注册,通过宏 PD_REGISTER_KERNEL 完成注册。

Kernel 函数直接操作 DenseTensor(或 SelectedRowsTensor 等),执行底层数学计算。GPU Kernel 会调用 CUDA/HIP kernel launch。

调试提示

  • 层①报错:通常是参数个数/类型不匹配,检查 Python 调用参数。
  • 层②报错:关注 GIL 释放/获取时序、Dist Tensor 转换逻辑。
  • 层③报错:反向图构建问题,检查 SetTensorWrapper/SetGradOutMeta/SetHistory 是否正确。
  • 层④报错:Kernel 未注册、数据类型不支持、PrepareData 转换失败。
  • 层⑤报错:Kernel 内部计算错误,需深入 PHI Kernel 实现。