|
1 | 1 | ##### 记录PaddleCPPAPITest仓库检测出来的接口不一致情况 |
2 | 2 |
|
3 | | -# Allocator 类与 torch 存在差异 |
| 3 | +# Allocator |
4 | 4 |
|
5 | 5 | ## 差异点列表 |
6 | 6 |
|
|
14 | 14 | --- |
15 | 15 |
|
16 | 16 | 涉及到的 PR:https://github.com/PFCCLab/PaddleCppAPITest/pull/42/changes#diff |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +# Device |
| 21 | + |
| 22 | +> Paddle 头文件:`c10\core\Device.h` |
| 23 | +
|
| 24 | +## 差异点列表 |
| 25 | + |
| 26 | +1. **未指定 Index 时的默认行为**:PyTorch index = -1,has_index() = false;Paddle 强制默认为 0,has_index() = true |
| 27 | +2. **纯字符串解析行为**:PyTorch 保持无索引状态(如 `cpu`、`cuda`);Paddle 自动补全为 0 号设备(如 `cpu:0`、`gpu:0`) |
| 28 | +3. **GPU/CUDA 字符串表示**:PyTorch 严格输出 `cuda` 或 `cuda:0`;Paddle 底层映射为 GPU,输出 `gpu:0` 或 `gpu:1` |
| 29 | +4. **底层类型枚举值(Enum ID)**:PyTorch CPU=0,CUDA=1;Paddle CPU=1,CUDA/GPU=2 |
| 30 | +5. **默认 Tensor 所在设备**:PyTorch 处于无明确索引的 cpu 状态;Paddle 明确挂载在 cpu:0 设备上 |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +提交的对齐 PR:https://github.com/PaddlePaddle/Paddle/pull/78066 |
| 35 | + |
| 36 | +--- |
| 37 | + |
| 38 | +# BFloat16 |
| 39 | + |
| 40 | +> Paddle 头文件:`c10\util\BFloat16.h` |
| 41 | +
|
| 42 | +## 差异点列表 |
| 43 | + |
| 44 | +1. **BFloat16 ScalarType 枚举值**:PyTorch 为 **11**,Paddle 为 **15** |
| 45 | +2. **ComplexFloat ScalarType 枚举值**:PyTorch 为 **8**,Paddle 为 **9** |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +# DefaultDtype |
| 50 | + |
| 51 | +> Paddle 头文件:`c10\core\DefaultDtype.h` |
| 52 | +
|
| 53 | +## 差异点列表 |
| 54 | + |
| 55 | +1. **BFloat16 枚举值**:PyTorch 为 **11**,Paddle 为 **15** |
| 56 | +2. **ComplexFloat(复数类型)枚举值**:PyTorch 为 **8**,Paddle 为 **9** |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +# IValue |
| 61 | + |
| 62 | +> Paddle 头文件:`ATen/core/ivalue.h` |
| 63 | +
|
| 64 | +## 差异点列表 |
| 65 | + |
| 66 | +1. **命名空间**:PyTorch 为 `c10::IValue`;Paddle 为 `torch::IValue`(c10 命名空间中不存在 IValue) |
| 67 | +2. **方法命名风格**:PyTorch 使用 camelCase(如 `isNone()`、`toBool()`);Paddle 使用 snake_case(如 `is_none()`、`to_bool()`) |
| 68 | +3. **`tagKind()` 方法**:PyTorch 存在;Paddle 中**不存在** |
| 69 | +4. **字符串提取方法**:PyTorch 为 `toStringRef()`;Paddle 为 `to_string()` |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | +# SparseTensor |
| 74 | + |
| 75 | +> Paddle 头文件:`ATen/ops/sparse_coo_tensor.h`、`ATen/ops/sparse_csr_tensor.h` |
| 76 | +
|
| 77 | +## 差异点列表 |
| 78 | + |
| 79 | +1. **sparse_coo_tensor 无 size 推断行为**:PyTorch 能根据 indices 内容正确推断完整 size(如 `2 2 2`);Paddle 推断结果第一个维度为 0(如 `0 2 2`) |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +# OptionalArrayRef |
| 84 | + |
| 85 | +> Paddle 头文件:`c10\util\OptionalArrayRef.h` |
| 86 | +
|
| 87 | +## 差异点列表 |
| 88 | + |
| 89 | +1. **运行时内存地址值**:两框架输出的内存地址不同(属正常运行时差异,不影响功能) |
| 90 | +2. **内部对象标识符**:两框架内部唯一标识符数值不同(属正常实现差异,不影响功能) |
| 91 | + |
| 92 | +> 注:OptionalArrayRef 核心功能(has_value、size、元素访问、reset、swap、emplace、slice 等)在两个框架中完全兼容,仅运行时地址和标识符存在差异。 |
| 93 | +
|
| 94 | +--- |
| 95 | + |
| 96 | +# at::indexing(Slice / EllipsisIndexType) |
| 97 | + |
| 98 | +> Paddle 头文件:`ATen/indexing.h` |
| 99 | +> PyTorch 头文件:`ATen/TensorIndexing.h` |
| 100 | +
|
| 101 | +## 差异点列表 |
| 102 | + |
| 103 | +1. **头文件路径不同**:PyTorch 为 `ATen/TensorIndexing.h`;Paddle compat 为 `ATen/indexing.h` |
| 104 | +2. **`Tensor::operator[](Slice)` 不支持**:PyTorch 的 `Tensor::operator[]` 接受 `at::indexing::Slice`;Paddle compat 的 `operator[]` 仅重载 `int64_t`,传入 `Slice` 会编译报错 |
| 105 | +3. **多维 Slice 索引写法不同**: |
| 106 | + - PyTorch:`t.index({Slice(0,2), Slice(1,3)})` —— 接受 `std::initializer_list<TensorIndex>` |
| 107 | + - Paddle:`t.index(std::vector<at::indexing::Slice>{Slice(0,2), Slice(1,3)})` —— 仅重载 `std::vector<Slice>` |
| 108 | +4. **`TensorIndex` 类不存在**:Paddle compat 的 `indexing.h` 未定义 `TensorIndex` 类,注释掉了 `index(ArrayRef<TensorIndex>)` 重载,仅保留 `index(const std::vector<Slice>&)` |
| 109 | + |
| 110 | + |
| 111 | +--- |
| 112 | + |
| 113 | +# ScalarType 扩展类型函数 |
| 114 | + |
| 115 | +> Paddle 头文件:`c10/core/ScalarType.h` |
| 116 | +
|
| 117 | +## 差异点列表 |
| 118 | + |
| 119 | +### 1. 量化类型 `elementSize` 未实现 |
| 120 | + |
| 121 | +`c10::elementSize()` 对量化整型不支持: |
| 122 | + |
| 123 | +| ScalarType | PyTorch 返回值 | Paddle 状态 | |
| 124 | +|------------|--------------|------------| |
| 125 | +| `QInt8` | 1 | 未实现,编译报错 | |
| 126 | +| `QUInt8` | 1 | 未实现,编译报错 | |
| 127 | +| `QInt32` | 4 | 未实现,编译报错 | |
| 128 | + |
| 129 | +### 2. Float8 扩展枚举值缺失 |
| 130 | + |
| 131 | +Paddle compat 的 `ScalarType` 枚举未定义以下两个值,`isFloat8Type` 实现中也将其注释掉: |
| 132 | + |
| 133 | +- `ScalarType::Float8_e5m2fnuz` |
| 134 | +- `ScalarType::Float8_e4m3fnuz` |
| 135 | + |
| 136 | +PyTorch 完整支持这两个 Float8 变体,Paddle compat 仅保留了 `Float8_e5m2` 和 `Float8_e4m3fn`。 |
| 137 | + |
| 138 | +### 3. `ComplexHalf` 枚举值缺失 |
| 139 | + |
| 140 | +Paddle compat 的 `ScalarType` 枚举未包含 `ComplexHalf`,`isComplexType` 实现中对该分支也已注释掉。PyTorch 完整支持。 |
| 141 | + |
| 142 | +### 4. 以下 10 个函数/常量在 Paddle compat 中完全缺失 |
| 143 | + |
| 144 | +整块 `#ifndef USE_PADDLE_API` 保护了如下 10 个测试,Paddle 下全部跳过: |
| 145 | + |
| 146 | +| 函数/常量 | 说明 | |
| 147 | +|-----------|------| |
| 148 | +| `c10::isQIntType()` | 判断量化整型 | |
| 149 | +| `c10::isBitsType()` | 判断位类型 | |
| 150 | +| `c10::isBarebonesUnsignedType()` | 判断裸无符号整型 | |
| 151 | +| `c10::toQIntType()` | 转换为量化整型 | |
| 152 | +| `c10::toUnderlying()` | 量化类型的底层类型 | |
| 153 | +| `c10::isUnderlying()` | 判断底层类型关系 | |
| 154 | +| `c10::toRealValueType()` | 复数类型转实数类型 | |
| 155 | +| `c10::toComplexType()` | 实数类型转复数类型 | |
| 156 | +| `c10::canCast()` | 类型间是否可转换 | |
| 157 | +| `c10::NumScalarTypes` | ScalarType 枚举总数常量 | |
| 158 | + |
| 159 | +## 修复方向 |
| 160 | + |
| 161 | +在 Paddle compat 的 `c10/core/ScalarType.h` 中逐一补全上述枚举值和函数实现,完成后将对应测试移出 `#ifndef USE_PADDLE_API` 块。 |
| 162 | + |
| 163 | +--- |
| 164 | + |
| 165 | +# TensorAccessor / GenericPackedTensorAccessor |
| 166 | + |
| 167 | +> Paddle 头文件:`ATen/core/TensorAccessor.h` |
| 168 | +
|
| 169 | +## 差异点列表 |
| 170 | + |
| 171 | +### `GenericPackedTensorAccessorBase` / `GenericPackedTensorAccessor` 系列类缺失 |
| 172 | + |
| 173 | +Paddle compat 的 `ATen/core/TensorAccessor.h` 中**未实现**以下类和类型别名: |
| 174 | + |
| 175 | +- `at::GenericPackedTensorAccessorBase<T, N, PtrTraits, index_t>` |
| 176 | +- `at::GenericPackedTensorAccessor<T, N, PtrTraits, index_t>` |
| 177 | +- `at::PackedTensorAccessor32<T, N, PtrTraits>`(`index_t = int32_t` 别名) |
| 178 | +- `at::PackedTensorAccessor64<T, N, PtrTraits>`(`index_t = int64_t` 别名) |
| 179 | + |
| 180 | +以及 `at::Tensor` 上的 `packed_accessor64<T,N>()` 方法(Paddle compat 仅有 `packed_accessor32`)。 |
| 181 | + |
| 182 | +libtorch 在同路径头文件中完整定义了上述类,供 CUDA kernel 使用。 |
| 183 | + |
| 184 | +## 修复方向 |
| 185 | + |
| 186 | +在 Paddle compat 的 `ATen/core/TensorAccessor.h` 中补充 `GenericPackedTensorAccessorBase`、`GenericPackedTensorAccessor` 完整实现及 `PackedTensorAccessor32/64` 类型别名;并在 `ATen/core/Tensor.h` 中补充 `packed_accessor64<T,N>()` 方法。 |
| 187 | + |
| 188 | +--- |
| 189 | + |
| 190 | +# Exception 宏(TORCH_CHECK_EQ / TORCH_CHECK_NE 失败语义差异) |
| 191 | + |
| 192 | +> Paddle 头文件:`c10/util/Exception.h` |
| 193 | +
|
| 194 | +## 差异点列表 |
| 195 | + |
| 196 | +1. **`TORCH_CHECK_EQ` 失败行为**:PyTorch 调用 `abort()` 终止进程(测试用 `EXPECT_DEATH` 捕获);Paddle 抛出 C++ 异常(测试用 try-catch 捕获)。 |
| 197 | +2. **`TORCH_CHECK_NE` 失败行为**:同上,两者失败行为不一致。 |
| 198 | + |
| 199 | +当前代码通过 `#if USE_PADDLE_API` 分叉两套检测逻辑以绕过差异,但这导致两个平台实际走不同测试路径,无法真正对比行为。 |
| 200 | + |
| 201 | +--- |
| 202 | + |
| 203 | +# CUDA Context(`at::cuda::getCurrentCUDAStream` 缺失) |
| 204 | + |
| 205 | +> Paddle 头文件:`ATen/cuda/CUDAContext.h`(Paddle compat 中不存在) |
| 206 | +
|
| 207 | +## 差异点列表 |
| 208 | + |
| 209 | +1. **`at::cuda::getCurrentCUDAStream()` 不存在**:Paddle compat 未提供该函数,整个调用块被 `#ifndef USE_PADDLE_API` 保护,Paddle 下只输出固定字符串 `"stream_skipped_paddle"`,无法进行真实对比。 |
| 210 | + |
| 211 | +--- |
| 212 | + |
| 213 | +# CUDA 工具类(CUDAGuard / CUDAStream / PhiloxCudaState 全部缺失) |
| 214 | + |
| 215 | +> Paddle 头文件:`c10/cuda/CUDAGuard.h`、`c10/cuda/CUDAStream.h`、`c10/cuda/PhiloxCudaState.h`(Paddle compat 中均不存在) |
| 216 | +> 测试文件:`test/CUDATest2.cpp` |
| 217 | +
|
| 218 | +## 差异点列表 |
| 219 | + |
| 220 | +以下类和相关头文件在 Paddle compat 中**完全缺失**,对应测试被 `#ifndef USE_PADDLE_API` 整块保护跳过: |
| 221 | + |
| 222 | +| 缺失类/结构 | 头文件 | |
| 223 | +|-------------|--------| |
| 224 | +| `c10::cuda::CUDAGuard` | `c10/cuda/CUDAGuard.h` | |
| 225 | +| `c10::cuda::OptionalCUDAGuard` | `c10/cuda/CUDAGuard.h` | |
| 226 | +| `c10::cuda::CUDAStream` | `c10/cuda/CUDAStream.h` | |
| 227 | +| `c10::cuda::getCurrentCUDAStream()` | `c10/cuda/CUDAStream.h` | |
| 228 | +| `c10::cuda::PhiloxCudaState` | `c10/cuda/PhiloxCudaState.h` | |
| 229 | + |
| 230 | +--- |
| 231 | + |
| 232 | +# TensorOptions(`requires_grad` 传递问题) |
| 233 | + |
| 234 | +> Paddle 头文件:`c10/core/TensorOptions.h` |
| 235 | +
|
| 236 | +## 差异点列表 |
| 237 | + |
| 238 | +1. **`at::empty()` 不支持含 `requires_grad` 的 `TensorOptions`**:Paddle 在通过 `at::empty({...}, opts)` 创建 tensor 时,若 `opts` 含有 `requires_grad(true)` 会抛出异常。PyTorch 完整支持。当前测试已绕过:将含 `requires_grad` 的 `opts` 与用于创建 tensor 的 `opts_for_dtype` 分离,单独测试 `requires_grad()` 的读取,但实际上 Paddle 无法通过 `TensorOptions` 在 tensor 创建时传递梯度需求。 |
| 239 | +2. **`device_index()` 对 CPU 设备的返回值不同**:Torch 对 CPU 设备返回 `-1`(无显式 index);Paddle 会将 CPU 规范化为 `cpu:0`,因此返回 `0`。 |
| 240 | + |
| 241 | +--- |
| 242 | + |
| 243 | +## 详细记录 |
| 244 | + |
| 245 | +- 测试用例:DeviceIndex |
| 246 | +- 字段:`c10::TensorOptions().device(c10::Device(c10::kCPU)).device_index()` |
| 247 | +- 差异: |
| 248 | + - Paddle 输出:`0` |
| 249 | + - Torch 输出:`-1` |
| 250 | +- 原因:Torch 将 CPU 设备视为无显式 index;Paddle 会将 CPU 设备规范化为 `cpu:0`。 |
| 251 | +- 处理:已在测试文件中注释掉该字段输出,并添加 `DIFF` 标注说明。 |
| 252 | + |
| 253 | +--- |
| 254 | + |
| 255 | +# Tensor::resize_(Paddle 不支持) |
| 256 | + |
| 257 | +> Paddle 头文件:`ATen/core/Tensor.h` |
| 258 | +
|
| 259 | +## 差异点列表 |
| 260 | + |
| 261 | +1. **`resize_()` 不支持**:Paddle 调用 `tensor.resize_({...})` 会抛出异常,PyTorch 完整支持原地调整 tensor 形状。当前测试用 try-catch 捕获异常并输出 `"1 "` 表示异常发生,无法对比实际 resize 结果。 |
| 262 | + |
| 263 | +--- |
| 264 | + |
| 265 | +# TensorFactoryTest |
| 266 | + |
| 267 | +## 差异点列表 |
| 268 | + |
| 269 | +1. **ScalarType::Bool 枚举值不同**:Paddle 的 DataType::BOOL = 10,Torch 的 ScalarType::Bool = 11。 |
| 270 | + |
| 271 | +--- |
| 272 | + |
| 273 | +## 详细记录 |
| 274 | + |
| 275 | +- 测试用例:TensorFromBoolArrayRef |
| 276 | +- 字段:scalar_type(write_tensor_info_to_file 输出的 static_cast<int>(t.scalar_type())) |
| 277 | +- 差异: |
| 278 | + - Paddle 输出:10 |
| 279 | + - Torch 输出:11 |
| 280 | +- 原因:Paddle 与 Torch 框架的 ScalarType::Bool 枚举值不同(Paddle=10,Torch=11),属于设计差异。 |
| 281 | +- 处理:已在测试文件中注释掉该字段输出,并添加 DIFF 标注说明。 |
| 282 | + |
| 283 | +--- |
| 284 | + |
| 285 | +# CUDADataTypeTest |
| 286 | + |
| 287 | +## 差异点列表 |
| 288 | + |
| 289 | +1. **`ScalarTypeToCudaDataType(Bool)` 支持范围不同**:Paddle compat 不支持 `Bool` 转 `cudaDataType`,会抛出异常;Torch 侧接口支持范围更完整。当前测试已跳过 `Bool`。 |
| 290 | +2. **`empty_cuda` 结果依赖运行时/构建环境**:Torch CUDA 版通常可成功创建 CUDA Tensor;Paddle compat 在未编译 CUDA 或运行时不可用时会抛异常并进入不可用分支。该差异属于环境差异,不属于接口语义差异。 |
| 291 | + |
| 292 | +--- |
| 293 | + |
| 294 | +## 详细记录 |
| 295 | + |
| 296 | +- 测试用例:GetCudaDataType |
| 297 | +- 字段:`Bool` 类型的 `ScalarTypeToCudaDataType` 转换 |
| 298 | +- 差异: |
| 299 | + - Paddle:抛出 `Cannot convert ScalarType Bool to cudaDataType` |
| 300 | + - Torch:可返回对应的 `cudaDataType` |
| 301 | +- 原因:Paddle compat 的 `ATen/cuda/CUDADataType.h` 未实现 `Bool` 分支。 |
| 302 | +- 处理:已在测试文件中跳过 `Bool` 的输出,并添加 `DIFF` 注释说明。 |
| 303 | + |
| 304 | +- 测试用例:EmptyCUDA / EmptyCudaDifferentDtype |
| 305 | +- 字段:结果字符串(`cuda_empty` / `cuda_empty_int` / `cuda_not_available`) |
| 306 | +- 差异: |
| 307 | + - Paddle 输出:`cuda_not_available` |
| 308 | + - Torch 输出:`cuda_empty`、`cuda_empty_int` |
| 309 | +- 原因:该结果依赖 Paddle 是否为 GPU 版以及当前 CUDA 运行时是否可用,属于运行时/构建环境差异,而非接口行为差异。 |
| 310 | +- 处理:已在测试文件中保留调用、注释掉结果字符串输出,并添加 `DIFF` 注释说明。 |
0 commit comments