评价此页

LibTorch 稳定 ABI#

创建于:2025 年 3 月 17 日 | 最后更新于:2026 年 1 月 6 日

概述#

LibTorch 稳定 ABI(应用程序二进制接口)提供了一个有限的接口,用于扩展 PyTorch 功能,而无需与特定的 PyTorch 版本紧密耦合。这使得开发可以在 PyTorch 版本之间保持兼容的自定义算子和扩展成为可能。这组有限的 API 并非旨在取代现有的 LibTorch,而是为了为大多数自定义扩展用例提供一个稳定的基础。如果您希望将任何 API 添加到稳定的 ABI 中,请通过 PyTorch 仓库中的新 issue 提出请求。

有限的稳定 ABI 由三个主要组件组成

  1. 稳定的 C 头文件 - 由 libtorch 实现的低级 C API(主要为 torch/csrc/inductor/aoti_torch/c/shim.h

  2. 仅头文件的 C++ 库 - 仅在头文件中实现的独立实用程序,因此不依赖于 libtorch (torch/headeronly/*)

  3. 稳定的 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(例如 getCurrentStreamDeviceGuard)。

我们正在不断改进 torch/csrc/stable API 中的覆盖范围。如果您希望在自定义扩展中支持特定的 API,请提交 issue。

有关稳定算子的完整 API 文档,请参阅 Torch 稳定 API cpp 文档

稳定的 C 头文件#

由 AOTInductor 启动的稳定 C 头文件构成了稳定 ABI 的基础。目前,可用的 C 头文件包括

保证这些头文件在版本之间保持 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/*.htorch/headeronly/*.h 和稳定的 C 头文件)。这意味着您的扩展程序不应使用任何来自 at::c10:: 命名空间的实用程序,而应使用它们在 torch::stabletorch::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 相关联。我们将在下面讨论以下内容

  1. StableIValue 转换

  2. StableIValue 堆栈约定

  3. 与调度器交互的稳定 API

StableIValue 转换#

我们为用户提供实用工具,以便使用在 torch/csrc/stable/stableivalue_conversions.h 中具有同义词 tofrom 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 算子。

  1. 自定义扩展中的类型:在最终用户自定义库中使用的类型。

  2. StableIValue 表示:以 ABI 稳定方式将类型转换为用户模型与 libtorch.so 之间的联络。

  3. libtorch 中的类型:在 libtorch.so(或与 libtorch 锁定的任何代码二进制文件)中使用的类型。

  4. 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/torch::headeronly::HeaderOnlyArrayRef

将原始按位复制到 uint64_t 的前导字节中,该指针指向一个新的 StableIValue,该 StableIValue 递归地表示底层元素列表。

c10::List<T>

Type[]

?

?

ivalue::Tuple<T>

(Type, …)

?

?

c10::SymInt

SymInt

?

?

c10::SymFloat

SymFloat

?

?

c10::SymBool

SymBool

?

?

at::QScheme

QScheme

堆栈约定#

堆栈有两个不变性

  1. 堆栈从左到右填充。a. 例如,表示参数 arg0arg1arg2 的堆栈将 arg0 放在索引 0 处,arg1 放在索引 1 处,arg2 放在索引 2 处。b. 返回值也从左到右填充,例如,ret0 位于索引 0 处,ret1 位于索引 1 处,依此类推。

  2. 堆栈始终拥有其持有的对象。a. 调用基于堆栈的 API 时,必须将拥有引用传递给调用堆栈并从返回堆栈窃取引用。b. 将您的函数注册为使用堆栈调用时,必须从您的参数堆栈窃取引用并推送到堆栈上的新引用。

基于堆栈的 API#

以上内容在两个地方相关

  1. STABLE_TORCH_LIBRARYTORCH_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));
    }
    
  2. 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 将调用由给定 opNameoverloadName、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/stabletorch/headeronly 中的 C++ API 遵循 PyTorch 的其余部分的 FC/BC 策略(请参阅 policy)。LibTorch ABI 稳定的 C shim API 保证至少有两年的兼容性窗口。