LibTorch 稳定 ABI#
创建于:2025 年 3 月 17 日 | 最后更新于:2026 年 1 月 6 日
概述#
LibTorch 稳定 ABI(应用程序二进制接口)提供了一个有限的接口,用于扩展 PyTorch 功能,而无需与特定的 PyTorch 版本紧密耦合。这使得开发可以在 PyTorch 版本之间保持兼容的自定义算子和扩展成为可能。这组有限的 API 并非旨在取代现有的 LibTorch,而是为了为大多数自定义扩展用例提供一个稳定的基础。如果您希望将任何 API 添加到稳定的 ABI 中,请通过 PyTorch 仓库中的新 issue 提出请求。
有限的稳定 ABI 由三个主要组件组成
稳定的 C 头文件 - 由 libtorch 实现的低级 C API(主要为
torch/csrc/inductor/aoti_torch/c/shim.h)仅头文件的 C++ 库 - 仅在头文件中实现的独立实用程序,因此不依赖于 libtorch (
torch/headeronly/*)稳定的 C++ 封装器 - 高级 C++ 便利封装器 (
torch/csrc/stable/*)
我们详细讨论了这些内容
torch/headeronly#
位于 torch/headeronly 的内联 C++ 头文件完全与 LibTorch 解耦。这些头文件包含某些自定义扩展编写者可能熟悉的实用程序。例如,c10::ScalarType enum 在这里作为 torch::headeronly::ScalarType 存在,以及 libtorch 独立的 TORCH_CHECK 版本,即 STD_TORCH_CHECK。您可以信任 torch::headeronly 命名空间中的所有 API 不依赖于 libtorch.so。这些 API 也在 torch/header_only_apis.txt 中全局列出。
torch/csrc/stable#
这是一组内联 C++ 头文件,它们提供围绕 C API 的封装器,以处理下面讨论的棘手问题。
它包括
torch/csrc/stable/library.h:提供 TORCH_LIBRARY 和类似宏的稳定版本。
torch/csrc/stable/tensor_struct.h:提供 torch::stable::Tensor,这是 at::Tensor 的稳定版本。
torch/csrc/stable/ops.h:提供一个稳定的接口,用于从
native_functions.yaml调用 ATen 算子。torch/csrc/stable/accelerator.h:提供一个稳定的接口,用于设备通用的对象和 API(例如
getCurrentStream、DeviceGuard)。
我们正在不断改进 torch/csrc/stable API 中的覆盖范围。如果您希望在自定义扩展中支持特定的 API,请提交 issue。
有关稳定算子的完整 API 文档,请参阅 Torch 稳定 API cpp 文档。
稳定的 C 头文件#
由 AOTInductor 启动的稳定 C 头文件构成了稳定 ABI 的基础。目前,可用的 C 头文件包括
torch/csrc/inductor/aoti_torch/c/shim.h:包含 C 样式的 shim API,用于常用 Tensor、dtype、CUDA 等。
torch/csrc/inductor/aoti_torch/generated/c_shim_aten.h:包含 C 样式的 shim API,用于来自
native_functions.yaml的 ATen 算子(例如aoti_torch_aten_new_empty)。torch/csrc/inductor/aoti_torch/generated/c_shim_*.h:包含 C 样式的 shim API,用于从
native_functions.yaml分派的特定后端内核(例如aoti_torch_cuda_pad)。这些 API 仅应用于它们命名的特定后端(例如aoti_torch_cuda_pad仅应在 CUDA 内核中使用),因为它们退出了分派器。torch/csrc/stable/c/shim.h:我们正在构建更多的 ABI,以逻辑上位于
torch/csrc/stable/c而不是继续不再适用于我们通用用例的 AOTI 命名。
保证这些头文件在版本之间保持 ABI 稳定,并遵守比 LibTorch 更严格的向后兼容性策略。具体来说,我们承诺在发布后至少 2 年内不修改它们。但是,这属于自行承担风险。例如,用户必须处理某些 API 返回对象的内存生命周期。此外,下面讨论的基于堆栈的 API 允许用户调用 PyTorch 分派器,但无法对所调用底层算子的向前和向后兼容性提供强有力的保证。
除非绝对必要,我们建议使用 torch/csrc/stable 中的高级 C++ API,它将为用户处理 C API 的所有棘手问题。
将您的内核迁移到 LibTorch 稳定 ABI#
如果您希望您的内核与 LibTorch 保持 ABI 稳定,这意味着您能够为某个版本构建并在另一个版本上运行,您的内核只能使用有限的稳定 ABI。以下部分介绍了一些迁移现有内核以及我们设想您需要交换的 API 的步骤。
首先,与其通过 TORCH_LIBRARY 注册内核,LibTorch ABI 稳定的内核必须通过 STABLE_TORCH_LIBRARY 注册。请注意,通过 STABLE_TORCH_LIBRARY 注册的实现必须是打包的,而 TORCH_LIBRARY 则不是。 TORCH_BOX 宏会自动处理大多数用例。请参阅下面的简单示例或我们的文档 基于堆栈的 API 以获取更多详细信息。对于通过 pybind 注册的内核,在使用稳定 ABI 之前,迁移到通过 TORCH_LIBRARY 注册它们将很有用。
虽然以前您的内核可能包含来自 <torch/*.h> 的 API(例如 <torch/all.h>),但现在它们仅限于包含来自上述 3 个类别的头文件(torch/csrc/stable/*.h、torch/headeronly/*.h 和稳定的 C 头文件)。这意味着您的扩展程序不应使用任何来自 at:: 或 c10:: 命名空间的实用程序,而应使用它们在 torch::stable 和 torch::headeronly 中的替代方案。为了提供一些必要的迁移示例
所有对
at::Tensor的使用都必须替换为torch::stable::Tensor所有对
TORCH_CHECK的使用都必须替换为STD_TORCH_CHECK所有对
at::kCUDA的使用都必须替换为torch::headeronly::kCUDA等。native 函数,例如
at::pad必须替换为torch::stable::pad作为 Tensor 方法调用的 native 函数(例如
Tensor.pad)必须通过torch::stable::pad替换为 ATen 变体。
如上所述,LibTorch 稳定 ABI 仍在开发中。如果您希望将任何 API 或功能添加到稳定 ABI/torch::headeronly/torch::stable,请通过 PyTorch 仓库中的新 issue 提出请求。
以下是迁移使用 TORCH_LIBRARY 的现有内核到稳定 ABI (TORCH_STABLE_LIBRARY) 的简单示例。有关更大的端到端示例,您可以查看 FA3 仓库。具体来说,flash_api.cpp 和稳定变体 flash_api_stable.cpp 之间的差异。
使用 TORCH_LIBRARY 的原始版本#
// original_kernel.cpp - Using TORCH_LIBRARY (not stable ABI)
#include <torch/torch.h>
#include <ATen/ATen.h>
namespace myops {
// Simple kernel that adds a scalar value to each element of a tensor
at::Tensor add_scalar(const at::Tensor& input, double scalar) {
TORCH_CHECK(input.scalar_type() == at::kFloat, "Input must be float32");
return input.add(scalar);
}
// Register the operator
TORCH_LIBRARY(myops, m) {
m.def("add_scalar(Tensor input, float scalar) -> Tensor");
}
// Register the implementation
TORCH_LIBRARY_IMPL(myops, CompositeExplicitAutograd, m) {
m.impl("add_scalar", &add_scalar);
}
} // namespace myops
使用 STABLE_TORCH_LIBRARY 的迁移版本#
// stable_kernel.cpp - Using STABLE_TORCH_LIBRARY (stable ABI)
// (1) Don't include <torch/torch.h> <ATen/ATen.h>
// only include APIs from torch/csrc/stable, torch/headeronly and C-shims
#include <torch/csrc/stable/library.h>
#include <torch/csrc/stable/tensor_struct.h>
#include <torch/csrc/stable/ops.h>
#include <torch/headeronly/core/ScalarType.h>
#include <torch/headeronly/macros/Macros.h>
namespace myops {
// Simple kernel that adds a scalar value to each element of a tensor
torch::stable::Tensor add_scalar(const torch::stable::Tensor& input, double scalar) {
// (2) use STD_TORCH_CHECK instead of TORCH_CHECK
STD_TORCH_CHECK(
// (3) use torch::headeronly::kFloat instead of at:kFloat
input.scalar_type() == torch::headeronly::kFloat,
"Input must be float32");
// (4) Use stable ops namespace instead of input.add
return torch::stable::add(input, scalar);
}
// (5) Register the operator using STABLE_TORCH_LIBRARY
STABLE_TORCH_LIBRARY(myops, m) {
m.def("add_scalar(Tensor input, float scalar) -> Tensor");
}
// (6) Register the implementation using STABLE_TORCH_LIBRARY_IMPL
// Use TORCH_BOX to automatically handle boxing/unboxing
STABLE_TORCH_LIBRARY_IMPL(myops, CompositeExplicitAutograd, m) {
m.impl("add_scalar", TORCH_BOX(&add_scalar));
}
} // namespace myops
当与分派器交互时,对象如何跨 ABI 边界传递?#
当通过稳定的 API(例如 STABLE_TORCH_LIBRARY 等)与调度器交互时,我们使用一种打包约定。参数和返回值表示为 StableIValue 的堆栈,这与 IValues 的 torch::jit::stack 相关联。我们将在下面讨论以下内容
StableIValue 转换
StableIValue 堆栈约定
与调度器交互的稳定 API
StableIValue 转换#
我们为用户提供实用工具,以便使用在 torch/csrc/stable/stableivalue_conversions.h 中具有同义词 to 和 from API 的方式将对象转换为 StableIValue 及其从 StableIValue 转换。我们记录了稳定的自定义扩展表示、libtorch 表示和 StableIValue 表示。我们有信心地支持的类型是表中已完成行的类型。您可以依赖于这个子集来实现适当的 ABI 稳定性,这意味着您可以对这些类型调用 to<T_custom_ext>(arg/ret) 或 from(T)。
对于有限的一组用例,我们还隐式支持任何可以在 64 位内表示的字面量类型作为 StableIValue,因为默认的 reinterpret_cast 将成功。(例如:c10::Device。)这些类型目前是尽力而为的 ABI 稳定,但将来可能会中断,因此仅应将其用于短期测试。
即使在自定义扩展中没有定义设备的标准表示,您也可以通过不检查 StableIValue 来在自定义内核中使用 StableIValue 抽象来处理诸如 c10::Device 之类的类型。例如,自定义算子可以将 StableIValue 设备作为参数,并直接将其传递给带有 aoti_torch_call_dispatcher 的 aten 算子。
自定义扩展中的类型:在最终用户自定义库中使用的类型。
StableIValue 表示:以 ABI 稳定方式将类型转换为用户模型与 libtorch.so 之间的联络。
libtorch 中的类型:在 libtorch.so(或与 libtorch 锁定的任何代码二进制文件)中使用的类型。
Schema 类型:如 schema 中描述的类型,我们将其视为 native_functions.yaml 中的 ATen ops 以及通过 TORCH_LIBRARY 或 torch.library 注册到调度器的用户定义自定义算子的真相来源。
自定义扩展中的类型 |
StableIValue 表示 |
libtorch 中的类型 |
Schema 类型 |
|---|---|---|---|
std::optional<S> |
如果存在值,则将原始按位复制到 uint64_t 的前导字节中,以表示 S 的新 StableIValue。如果不存在值,则为 nullptr。 |
std::optional<T> |
类型? |
torch::stable::Tensor |
将底层 AtenTensorHandle 的原始按位复制到 uint64_t 的前导字节中 |
at::Tensor |
张量 |
torch::headeronly::ScalarType |
将翻译后的底层枚举的原始按位复制到 uint64_t 的前导字节中 |
torch::headeronly::ScalarType |
ScalarType |
torch::headeronly::Layout |
将翻译后的底层枚举的原始按位复制到 uint64_t 的前导字节中 |
at::Layout |
Layout |
torch::headeronly::MemoryFormat |
将翻译后的底层枚举的原始按位复制到 uint64_t 的前导字节中 |
at::MemoryFormat |
MemoryFormat |
布尔值 |
将原始按位复制到 uint64_t 的前导字节中 |
布尔值 |
布尔值 |
int64_t |
将原始按位复制到 uint64_t 的前导字节中 |
int64_t |
int |
double |
将原始按位复制到 uint64_t 的前导字节中 |
double |
浮点数 |
torch::stable::Device |
将索引和类型以原始按位复制的方式复制到 uint64_t 的前导字节中 |
c10::Device |
设备 |
? |
? |
c10::Stream |
流 |
? |
? |
c10::complex |
complex |
? |
? |
at::Scalar |
Scalar |
std::string/std::string_view |
将底层 StringHandle 的原始按位复制到 uint64_t 的前导字节中 |
std::string/const char*/ivalue::ConstantString |
str |
? |
? |
at::Storage |
Storage |
? |
? |
at::Generator |
生成器 |
std::vector |
将原始按位复制到 uint64_t 的前导字节中,该指针指向一个新的 StableIValue,该 StableIValue 递归地表示底层元素列表。 |
c10::List<T> |
Type[] |
? |
? |
ivalue::Tuple<T> |
(Type, …) |
? |
? |
c10::SymInt |
SymInt |
? |
? |
c10::SymFloat |
SymFloat |
? |
? |
c10::SymBool |
SymBool |
? |
? |
at::QScheme |
QScheme |
堆栈约定#
堆栈有两个不变性
堆栈从左到右填充。a. 例如,表示参数
arg0、arg1和arg2的堆栈将arg0放在索引 0 处,arg1放在索引 1 处,arg2放在索引 2 处。b. 返回值也从左到右填充,例如,ret0位于索引 0 处,ret1位于索引 1 处,依此类推。堆栈始终拥有其持有的对象。a. 调用基于堆栈的 API 时,必须将拥有引用传递给调用堆栈并从返回堆栈窃取引用。b. 将您的函数注册为使用堆栈调用时,必须从您的参数堆栈窃取引用并推送到堆栈上的新引用。
基于堆栈的 API#
以上内容在两个地方相关
STABLE_TORCH_LIBRARY与TORCH_LIBRARY不同,调度器期望通过STABLE_TORCH_LIBRARY注册的内核被打包。TORCH_BOX宏会自动为您处理此打包。Tensor my_amax_vec(Tensor t) { std::vector<int64_t> v = {0,1}; return amax(t, v, false); } // Use TORCH_BOX to automatically generate the boxed wrapper STABLE_TORCH_LIBRARY(myops, m) { m.def("my_amax_vec(Tensor t) -> Tensor", TORCH_BOX(&my_amax_vec)); }
torch_call_dispatcher此 API 允许您从 C/C++ 代码调用 PyTorch 调度器。它的签名如下torch_call_dispatcher(const char* opName, const char* overloadName, StableIValue* stack, uint64_t extension_build_version);
torch_call_dispatcher将调用由给定opName、overloadName、StableIValue 堆栈和用户扩展的TORCH_ABI_VERSION定义的 op 重载。此调用会将 op 的任何返回值填充到堆栈中的 StableIValue 形式,ret0位于索引 0 处,ret1位于索引 1 处,依此类推。我们警告不要使用此 API 调用已由其他扩展注册到调度器的函数,除非调用者可以保证他们期望的签名与自定义扩展注册的签名匹配。
版本控制和向前/向后兼容性保证#
我们在 torch/headeronly/version.h 中提供了一个 TORCH_ABI_VERSION 宏,形式如下
[ byte ][ byte ][ byte ][ byte ][ byte ][ byte ][ byte ][ byte ]
[MAJ ][ MIN ][PATCH ][ ABI TAG ]
在开发的当前阶段,C-shim 中的 API 将根据它们首次引入的主要.次要.补丁版本进行版本控制,2.10 将是首次强制执行此操作的版本。ABI 标签保留供将来使用。
扩展可以使用以下方式选择要与之兼容的最低 abi 版本
#define TORCH_TARGET_VERSION (((0ULL + major) << 56) | ((0ULL + minor) << 48))
在包含任何稳定的头文件之前,或将等效的 -D 选项传递给编译器。否则,默认值为当前的 TORCH_ABI_VERSION。
以上确保了如果用户将 TORCH_TARGET_VERSION 定义为 0x0209000000000000 (2.9) 并尝试使用在版本 2.10 中引入的 C shim API foo,将引发编译错误。 同样,torch/csrc/stable 中的 C++ 包装器 API 与 libtorch 二进制文件兼容,直到它们公开的 TORCH_ABI_VERSION,并与较新的 libtorch 二进制文件向前兼容。
torch/csrc/stable 或 torch/headeronly 中的 C++ API 遵循 PyTorch 的其余部分的 FC/BC 策略(请参阅 policy)。LibTorch ABI 稳定的 C shim API 保证至少有两年的兼容性窗口。