| name | compatibility-testing |
| description | PaddlePaddle 与 PyTorch C++ API 兼容性测试开发规范。适用于:编写或扩展 test/ 目录下的兼容性测试、验证 Paddle 兼容层与 PyTorch 同一 API 的行为一致性、定位接口输出差异、新增算子测试、覆盖 Shape/Dtype/值域/API 变体。Use when: writing compatibility tests, adding operator tests, checking ATen/c10 API behavior differences between Paddle and PyTorch. |
| argument-hint | 要测试的算子名称或 API,例如 abs、sum、reshape |
compatibility-testing
PaddlePaddle 与 PyTorch C++ API 兼容性测试开发规范。
上游调用上下文
本 skill 可被独立调用(用户直接传入算子名按完整工作流走),也可被上游驱动型
skill 在写测试步骤调用:
| 上游 skill | 调用时机 | 期望传入字段 | 期望返回 |
|---|
| add-compat-api | Step 2 第 4 子项(新增 PCAT 测试) | PCAT_ROOT / 算子名 / 覆盖目标 / 输出路径 | 测试文件骨架 + 强制项 checklist 自检结论 |
| fix-compat-api | Step 3 第 3 子项(最小修复涉及 PCAT 测试) | PCAT_ROOT / 算子名 / 修复点 / 已有测试路径 | 针对修复点的最小化用例片段 + 与已有用例的合并建议 |
被上游调用时,本 skill 不反向调用任何上游 skill,也不触发其他 skill,
只产出测试代码与 checklist 结论,控制权立即交还上游。
被上游调用时,"作为参考"的样例不必完整重写,只针对传入的"覆盖目标"或
"修复点"给出增量代码,避免与已有测试重复。
触发条件
适用场景:
- 编写或扩展
PaddleCppAPITest\test 下的兼容性测试
- 验证 Paddle 兼容层与 PyTorch 对同一 API 的行为一致性
- 定位某个接口在两个框架间的输出差异
测试目标
测试范围:覆盖 Paddle\paddle\phi\api\include\compat 目录下所有接口,包括但不限于:
| 目录 | 接口类型 | 示例 |
|---|
ATen/ops/ | ATen 算子 | abs.h, sum.h, reshape.h, zeros.h ... |
ATen/core/ | ATen 核心类型 | Tensor.h, TensorBody.h, TensorAccessor.h ... |
ATen/ | ATen 基础 | Tensor.h, Device.h, DeviceGuard.h ... |
c10/core/ | C10 核心 | ScalarType.h, TensorOptions.h, Storage.h ... |
c10/util/ | C10 工具 | Optional.h, ArrayRef.h, Half.h ... |
c10/cuda/ | C10 CUDA | CUDAStream.h, CUDAGuard.h, CUDAException.h ... |
torch/ | Torch 包装 | all.h, cuda.h, extension.h ... |
utils/ | 工具函数 | scalar_type_conversion.h, int_array_ref_conversion.h ... |
AbsTest.cpp(位于 test/ops/ 仅为示例)仅作为参考,展示测试文件结构和输出格式。
项目约定
- 构建系统通过
CMakeLists.txt 中的 create_paddle_tests() 函数同时生成 torch_* 和 paddle_* 两套可执行文件
- 测试二进制运行时自动以自身文件名命名输出文件(如
torch_AbsTest.txt),由 main.cpp 中的 g_custom_param 传递
- 结果对比依赖文本 diff,因此输出格式的确定性至关重要
测试文件结构
文件头与命名空间
测试文件统一位于 PaddleCppAPITest\test,与 compat 接口目录结构对应。参考以下结构(以 AbsTest.cpp 为示例):
#include <ATen/ATen.h>
#include <ATen/core/Tensor.h>
#include <ATen/ops/abs.h>
#include <ATen/ops/zeros.h>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include "../../src/file_manager.h"
extern paddle_api_test::ThreadSafeParam g_custom_param;
namespace at {
namespace test {
using paddle_api_test::FileManerger;
using paddle_api_test::ThreadSafeParam;
class AbsTest : public ::testing::Test {
protected:
void SetUp() override {
}
at::Tensor test_tensor;
};
}
}
关键约束:
- 命名空间固定为
at::test,保证与 ATen 类型系统的直接可见性
g_custom_param 是全局线程安全参数,存储当前运行的输出文件名,由 main.cpp 在 RUN_ALL_TESTS() 前注入
- 测试类命名格式
<OpName>Test,文件名与之一致
结果输出函数
每个测试文件包含一个静态输出函数,负责将 tensor 结果序列化到文件。该函数是跨框架对比的唯一数据源,格式必须确定且可复现:
static void write_abs_result_to_file(FileManerger* file, const at::Tensor& result) {
*file << std::to_string(result.dim()) << " ";
*file << std::to_string(result.numel()) << " ";
float* data = result.data_ptr<float>();
for (int64_t i = 0; i < result.numel(); ++i) {
*file << std::to_string(data[i]) << " ";
}
}
注意:
- 第一个测试用例调用
file.createFile() 创建文件,后续用例调用 file.openAppend() 追加
- 每个用例输出前须写入用例名标签,输出末尾须追加
"\n" 换行,使每个用例占独立一行(详见"输出格式"章节)
- 输出函数参数使用
FileManerger*(指针),调用处传 &file。不能使用非 const 引用,否则违反 Google C++ 规范(cpplint runtime/references)
- 对于多 dtype 支持的算子,需按
result.scalar_type() 分发到对应的 data_ptr<T>() 类型
Shape 覆盖要求
测试 shape 的选择直接影响边界条件的暴露率。以下为四个必选维度区间,每个新算子测试须至少各取一例:
标量 (0-d tensor)
{} — 零维标量,部分算子(如 sum 不指定 dim)的返回类型
- 注意:
{1} 是 1-d tensor,不是标量
小 shape(元素数 ≤ 64)
- 典型值:
{4}、{2, 3}、{2, 3, 4}
- 便于手工验证数值正确性
大 shape(元素数 ≥ 10000)
- 典型值:
{10000}、{100, 100}、{10, 20, 30, 40}
- 主要暴露精度累积误差和内存布局差异
边界 shape
- 含零维度:
{0}、{2, 0}、{1, 0, 3} — 验证空 tensor 语义
- 全一维度:
{1, 1, 1} — 常触发 squeeze/broadcast 的特殊路径
- 经
transpose() / as_strided() 产生的非连续 tensor — 验证 stride 处理的正确性
Dtype 覆盖要求
以下为 ATen 支持的标准标量类型,通过 at::TensorOptions().dtype() 或 shorthand 常量指定。新增测试至少需要覆盖 kFloat、kDouble、kInt、kLong 四种基础类型,其余按算子语义酌情补充:
| 标量类型 | ATen 常量 | C++ 对应类型 | 适用注意 |
|---|
| float32 | at::kFloat | float | 多数算子的默认 dtype |
| float64 | at::kDouble | double | 精度基准,常用于 reference 比较 |
| int32 | at::kInt | int32_t | 整型算子、索引 |
| int64 | at::kLong | int64_t | shape / dim 参数的底层类型 |
| int16 | at::kShort | int16_t | 较少使用,部分量化场景 |
| int8 | at::kChar | int8_t | 不要与 kByte (uint8) 混淆 |
| uint8 | at::kByte | uint8_t | 常见于图像数据 |
| bool | at::kBool | bool | 比较算子的返回类型 |
Paddle 兼容层的 dtype 映射与 PyTorch 存在细微差异(例如默认 dtype 可能不同),输出对比时需关注此类隐式转换。
异常行为测试
部分算子在非法输入下的异常行为可能在两个框架间存在差异(一个抛异常、另一个返回 NaN 或空 tensor)。此类差异需显式捕获并记录:
TEST_F(SomeOpTest, InvalidInputHandling) {
auto file_name = g_custom_param.get();
FileManerger file(file_name);
file.openAppend();
file << "InvalidInputHandling ";
try {
at::Tensor result = at::some_op(invalid_tensor);
write_someop_result_to_file(&file, result);
} catch (const std::exception& e) {
file << "exception ";
}
file << "\n";
file.saveFile();
}
注意事项:
- 不要使用
c10::Error 作为 catch 类型 — 在 Paddle 兼容层中 c10::Error 是 C10ErrorType 枚举常量(定义在 c10/util/Exception.h),不是异常类。统一使用 std::exception 捕获即可。
- 不要输出
e.what() — 两个框架的异常消息文本不同,会产生大量无意义的 diff。仅记录 "exception " 标记是否抛出异常。
- 两框架的对比重点是是否抛异常须一致,异常消息内容不要求匹配。
输出格式
输出文件采用空格分隔的纯文本,每个用例独占一行,格式如下:
<TestCaseName> <ndim> <numel> [<size_0> <size_1> ...] <val_0> <val_1> ...\n
- 用例名标签:每行以用例名(如
SumAllElements)开头,与后续数据以空格分隔
- 换行分隔:每个用例输出末尾追加
"\n",使 diff 能逐行定位到具体出错的用例
示例(SumTest 的部分输出):
SumAllElements 0 1 21.000000
SumWithDtype 7 0 1 21.000000
SumAlongDim0 1 3 3 5.000000 7.000000 9.000000
SumInt32 0 1 100
代码示例:
TEST_F(SumTest, SumAllElements) {
auto file_name = g_custom_param.get();
FileManerger file(file_name);
file.createFile();
file << "SumAllElements ";
at::Tensor result = at::sum(test_tensor);
write_sum_result_to_file(&file, result);
file << "\n";
file.saveFile();
}
TEST_F(SumTest, SumWithDtype) {
auto file_name = g_custom_param.get();
FileManerger file(file_name);
file.openAppend();
file << "SumWithDtype ";
file << "\n";
file.saveFile();
}
注意事项:
- 浮点值通过
std::to_string() 序列化,精度为 6 位有效数字
- 用例名标签使得 diff 输出直接可读,无需逐字节计数来定位差异
- 不同测试用例的输出依次追加到同一文件中,顺序由 GTest 的用例注册顺序决定
- Place的验证可以取HashValue()
- Device的比较可以取str()
- 如果./test/result_cmp.sh的对比结果有差异,请记录下来,在最后总结告诉我,不需要修改测试代码
仅运行单个测试
./torch/torch_AbsTest --gtest_filter="AbsTest.EdgeValues"
运行对比脚本
cd .. && ./test/result_cmp.sh build
新算子测试检查清单
新增测试前逐项确认,标注 * 的为强制项:
Shape 维度
Dtype
值域
API 变体
输出
输出文件路径
默认输出目录:/tmp/paddle_cpp_api_test/(由 FileManerger::basic_path_ 控制)。
文件名自动取可执行文件名 + .txt:
torch_AbsTest → /tmp/paddle_cpp_api_test/torch_AbsTest.txt
paddle_AbsTest → /tmp/paddle_cpp_api_test/paddle_AbsTest.txt
如需自定义路径,在构造 FileManerger 时传入完整文件名即可覆盖(但通常不建议,以保持批量对比脚本的兼容性)。