Commit 01e32e53 authored by liuys's avatar liuys 🏸
Browse files

update

parent 77afe6c8
Pytorch 自定义算子的实现 # PyTorch 自定义算子工程
- 1part: 本工程展示了两种不同的 PyTorch 自定义算子实现方法,分别使用 PyBind11 和 TORCH_LIBRARY 机制。
test_torch_library_expand.cpp 中, 一个同时存在 TORCH_LIBRARY 下同时存在 define 和 impl; ## 工程结构
PyTorch 的设计允许: ```
torch_custom_op/
├── pybind11_custom_op/ # 使用 PyBind11 实现的自定义算子
└── torch_library_custom_op/ # 使用 TORCH_LIBRARY 实现的自定义算子
```
## 子项目说明
### 1. pybind11_custom_op
使用 PyBind11 将 C++ 实现的算子暴露给 Python,并在 C++ 层面实现 CPU 与 CUDA 的手动分发逻辑。
**特点**
- 直接使用 PyBind11 绑定 C++ 函数
- 通过检查输入张量的设备类型手动调度执行后端
- 适合需要更灵活控制的场景
简单情况:直接在 TORCH_LIBRARY 中同时完成定义和默认实现 **关键文件**
- `test_pybind_expand.cpp`:C++ 源码,包含 CPU/CUDA 实现及 PyBind11 绑定
- `setup.py`:构建脚本,基于 setuptools 和 torch.utils.cpp_extension
- `test_pybind_expand.py`:测试脚本,验证算子正确性及 CUDA 支持
- `build.sh`:自动化脚本,清理、编译、安装并运行测试
复杂情况:使用 TORCH_LIBRARY_IMPL 为不同设备(CPU/CUDA)或后端提供专门的实现 ### 2. torch_library_custom_op
- 2part: 使用 PyTorch 官方的 TORCH_LIBRARY 机制实现自定义算子,支持为不同设备提供专门的实现。
> TORCH_LIBRARY 和 TORCH_LIBRARY_IMPL 的在多个CPP实现; **特点**
- 使用 PyTorch 官方推荐的 TORCH_LIBRARY 注册机制
- 支持为不同设备(CPU/CUDA)提供专门的实现
- 自动处理设备分发,无需手动检查设备类型
test_torch_library_expand.cpp 包含 PyInit_test_ops 的初始化以及创建; **关键文件**
- `custom_op/test_torch_library_expand.cpp`:定义算子接口和初始化 Python 模块
- `custom_op/test_ops_impl.cpp`:实现算子逻辑并注册到不同设备
- `custom_op/CMakeLists.txt`:使用 CMake 配置编译
- `custom_op/setup.py`:使用 setup.py 配置编译和安装
- `custom_op/build.sh`:编译脚本
支持2种编译方法: ## 快速开始
1. `bash build.sh` ### 环境准备
2. pip 安装: 使用以下镜像创建容器:
```bash ```bash
pip install --no-build-isolation . docker pull harbor.sourcefind.cn:5443/dcu/admin/base/vllm:0.11.0-ubuntu22.04-dtk25.04.2-1226-das1.7-py3.10-20251226
python3 test_torch_library_expand.py
``` ```
- 3part: ### 使用方法
PyInit 的模块名字发生变化;
build.sh 方式编译的导入到python需要使用 test_torch_library_expand_build_sh.py
- 4part:
test_torch_library_expand.cpp 没有包含
```c++
// // Python模块初始化函数
// extern "C" {
// PyObject *PyInit_test_ops(void) {
// static struct PyModuleDef module_def = {
// PyModuleDef_HEAD_INIT,
// "test_ops", // 模块名
// "Test operations module", // 文档
// -1,
// NULL // 方法定义
// };
// return PyModule_Create(&module_def);
// }
// }
```
报错 #### 对于 pybind11_custom_op
```bash
cd pybind11_custom_op
bash build.sh
``` ```
加载扩展失败: dynamic module does not define module export function (PyInit_test_ops)
请先编译 C++ 扩展 #### 对于 torch_library_custom_op:
```
\ No newline at end of file 支持两种编译方法:
1. 使用 build.sh 编译:
```bash
cd torch_library_custom_op/custom_op
bash build.sh
```
2. 使用 pip 安装:
```bash
cd torch_library_custom_op/custom_op
pip install --no-build-isolation .
cd test
python test_torch_library_expand_pip_install.py
```
## 两种实现方法对比
| 特性 | PyBind11 实现 | TORCH_LIBRARY 实现 |
|------|--------------|-------------------|
| 设备分发 | 手动检查设备类型 | 自动处理设备分发 |
| 注册机制 | 直接绑定 C++ 函数 | 使用 TORCH_LIBRARY 宏注册 |
| 灵活性 | 更高,可自定义绑定逻辑 | 更规范,符合 PyTorch 设计 |
| 集成度 | 与 PyTorch 生态集成度较低 | 与 PyTorch 生态完全集成 |
| 适用场景 | 需要更灵活控制的场景 | 标准 PyTorch 自定义算子开发 |
## 测试
每个子项目都包含测试脚本,用于验证算子的正确性和性能:
- `pybind11_custom_op/test_pybind_expand.py`
- `torch_library_custom_op/custom_op/test_torch_library_expand_build_sh.py`
- `torch_library_custom_op/test/test_torch_library_expand_pip_install.py`
## 总结
本工程展示了两种不同的 PyTorch 自定义算子实现方法,各有优缺点:
- **PyBind11 实现**:适合需要更灵活控制的场景,可自定义绑定逻辑,但需要手动处理设备分发。
- **TORCH_LIBRARY 实现**:符合 PyTorch 官方推荐的做法,自动处理设备分发,与 PyTorch 生态完全集成。
开发者可以根据具体需求选择适合的实现方法。
\ No newline at end of file
# pybind_custom_op
PyTorch Custom Operator with PyBind11 (Manual Dispatch)
这是一个 PyTorch 自定义算子(Custom Operator)的最小化示例项目。
本项目展示了如何使用 PyBind11 将 C++ 实现的算子暴露给 Python,并在 C++ 层面实现 CPU 与 CUDA 的手动分发(Manual Dispatch) 逻辑。
#### 项目简介
与标准的 TORCH_LIBRARY 注册机制不同,本项目使用 pybind11 直接绑定 C++ 函数。由于 pybind11 本身不具备 PyTorch 的自动设备分发功能,我们在 C++ 代码中通过检查输入张量的设备类型 (input.device().is_cuda()) 来手动调度执行后端。
#### 文件结构
```text
.
├── test_pybind_expand.cpp # C++ 源码:包含 CPU/CUDA 实现及 PyBind11 绑定
├── setup.py # 构建脚本:基于 setuptools 和 torch.utils.cpp_extension
├── test_pybind_expand.py # 测试脚本:验证算子正确性及 CUDA 支持
├── build.sh # 自动化脚本:清理、编译、安装并运行测试
└── README.md # 项目说明文档
```
#### 快速开始
使用以下镜像创建容器:
```bash
docker pull harbor.sourcefind.cn:5443/dcu/admin/base/vllm:0.11.0-ubuntu22.04-dtk25.04.2-1226-das1.7-py3.10-20251226
```
1. 编译并运行
在项目根目录下执行:
bash build.sh
2. 脚本执行流程:
清理旧的 build/ 目录和 .so 文件。
运行 python setup.py install 编译 C++ 扩展。
将编译生成的 .so 动态库复制到当前目录。
运行 python test_pybind_expand.py 进行验证。
\ No newline at end of file
#!/bin/bash
# 清理旧产物
rm -r build
rm -r *.egg-info
rm -rf *.so
# 编译模块
python setup.py install
# 复制编译后的库到当前目录
cp ./build/lib.linux-x86_64-cpython-310/my_pybind_ops.cpython-310-x86_64-linux-gnu.so .
# 运行测试
python test_pybind_expand.py
from setuptools import setup
from torch.utils.cpp_extension import BuildExtension, CppExtension
setup(
name='my_pybind_ops', # 编译后的包名
ext_modules=[
CppExtension(
name='my_pybind_ops', # 扩展模块名(import 时用的名字)
sources=['test_pybind_expand.cpp'], # C++ 源文件列表
),
],
cmdclass={
'build_ext': BuildExtension # 使用 PyTorch 提供的构建类
}
)
\ No newline at end of file
#include <torch/extension.h>
#define TORCH_HAS_CUDA
// CPU 实现
torch::Tensor add_one_cpu(torch::Tensor input) {
std::cout << "[Backend] Calling CPU implementation" << std::endl;
return input + 1;
}
// CUDA 实现
torch::Tensor add_one_cuda(torch::Tensor input) {
std::cout << "[Backend] Calling CUDA implementation" << std::endl;
return input + 1;
}
// 根据设备类型,调度给CPU或DCU执行
torch::Tensor add_one(torch::Tensor input) {
// 手动检查设备类型
if (input.device().is_cuda()) {
#ifdef TORCH_HAS_CUDA
return add_one_cuda(input);
#else
AT_ERROR("Not compiled with CUDA support");
#endif
} else {
return add_one_cpu(input);
}
}
// PyBind11 绑定
PYBIND11_MODULE(my_pybind_ops, m) {
m.doc() = "Manual dispatch example";
m.def("add_one", &add_one, "Add one (Auto dispatch)");
}
\ No newline at end of file
...@@ -9,8 +9,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) ...@@ -9,8 +9,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try: try:
# 注意:实际使用时,你需要先编译 C++ 扩展 # 注意:实际使用时,你需要先编译 C++ 扩展
# 这里我们假设扩展已经编译并可用 # 这里我们假设扩展已经编译并可用
import test_ops import my_pybind_ops
print("成功加载 test_ops 扩展") print("成功加载 my_pybind_ops 扩展")
except ImportError as e: except ImportError as e:
print(f"加载扩展失败: {e}") print(f"加载扩展失败: {e}")
print("请先编译 C++ 扩展") print("请先编译 C++ 扩展")
...@@ -25,7 +25,7 @@ def test_add_one(): ...@@ -25,7 +25,7 @@ def test_add_one():
print(f"输入: {x}") print(f"输入: {x}")
# 调用自定义操作符 # 调用自定义操作符
y = torch.ops.test_ops.add_one(x) y = my_pybind_ops.add_one(x)
print(f"输出: {y}") print(f"输出: {y}")
# 验证结果 # 验证结果
...@@ -33,23 +33,6 @@ def test_add_one(): ...@@ -33,23 +33,6 @@ def test_add_one():
assert torch.allclose(y, expected), f"结果不匹配: {y} vs {expected}" assert torch.allclose(y, expected), f"结果不匹配: {y} vs {expected}"
print("✓ 测试通过") print("✓ 测试通过")
def test_multiply_by_two():
"""测试 multiply_by_two 操作符"""
print("\n测试 multiply_by_two 操作符:")
# 创建测试张量
x = torch.tensor([1.0, 2.0, 3.0])
print(f"输入: {x}")
# 调用自定义操作符
y = torch.ops.test_ops.multiply_by_two(x)
print(f"输出: {y}")
# 验证结果
expected = x * 2
assert torch.allclose(y, expected), f"结果不匹配: {y} vs {expected}"
print("✓ 测试通过")
def test_cuda_support(): def test_cuda_support():
"""测试 CUDA 支持(如果可用)""" """测试 CUDA 支持(如果可用)"""
if torch.cuda.is_available(): if torch.cuda.is_available():
...@@ -60,25 +43,19 @@ def test_cuda_support(): ...@@ -60,25 +43,19 @@ def test_cuda_support():
print(f"CUDA 输入: {x}") print(f"CUDA 输入: {x}")
# 测试 add_one # 测试 add_one
y = torch.ops.test_ops.add_one(x) y = my_pybind_ops.add_one(x)
print(f"add_one 输出: {y}") print(f"add_one 输出: {y}")
assert torch.allclose(y, x + 1), f"CUDA add_one 结果不匹配" assert torch.allclose(y, x + 1), f"CUDA add_one 结果不匹配"
# 测试 multiply_by_two
z = torch.ops.test_ops.multiply_by_two(x)
print(f"multiply_by_two 输出: {z}")
assert torch.allclose(z, x * 2), f"CUDA multiply_by_two 结果不匹配"
print("✓ CUDA 测试通过") print("✓ CUDA 测试通过")
else: else:
print("\nCUDA 不可用,跳过 CUDA 测试") print("\nCUDA 不可用,跳过 CUDA 测试")
if __name__ == "__main__": if __name__ == "__main__":
print("测试 TORCH_LIBRARY_EXPAND 示例") print("测试 PYBIND_EXPAND 示例")
print("=" * 50) print("=" * 50)
test_add_one() test_add_one()
test_multiply_by_two()
test_cuda_support() test_cuda_support()
print("\n" + "=" * 50) print("\n" + "=" * 50)
......
Pytorch 自定义算子的实现
PyTorch 的设计允许:
简单情况:直接在 TORCH_LIBRARY 中同时完成定义和默认实现
复杂情况:使用 TORCH_LIBRARY_IMPL 为不同设备(CPU/CUDA)或后端提供专门的实现
---
快速开始:
使用以下镜像创建容器:
```bash
docker pull harbor.sourcefind.cn:5443/dcu/admin/base/vllm:0.11.0-ubuntu22.04-dtk25.04.2-1226-das1.7-py3.10-20251226
```
支持2种编译方法:
1. `bash build.sh`
2. pip 安装:
```bash
pip install --no-build-isolation .
cd test
python test_torch_library_expand_pip_install.py
```
build.sh 方式编译的导入到python需要使用 test_torch_library_expand_build_sh.py
\ No newline at end of file
...@@ -8,15 +8,15 @@ mkdir -p $BUILD_DIR ...@@ -8,15 +8,15 @@ mkdir -p $BUILD_DIR
cd $BUILD_DIR cd $BUILD_DIR
# 运行 CMake # 运行 CMake
cmake .. cmake ..
# 编译 # 编译
cmake --build . cmake --build .
# 复制编译后的库到当前目录 # 复制编译后的库到当前目录
cp test_ops.so .. cp *.so ..
cd .. cd ..
# 运行测试 # 运行测试
python test_torch_library_expand.py python test_torch_library_expand_build_sh.py
...@@ -18,74 +18,89 @@ PyTorch 提供了强大的自定义算子开发能力,通过 `TORCH_LIBRARY` ...@@ -18,74 +18,89 @@ PyTorch 提供了强大的自定义算子开发能力,通过 `TORCH_LIBRARY`
- Python 3.6+ - Python 3.6+
### 2.2 项目结构 ### 2.2 项目结构
推荐的项目结构如下: 实际项目结构如下:
``` ```
my_custom_ops/ custom_op/
├── setup.py # Python 包配置 ├── build/ # 编译目录
├── build.sh # 编译脚本 ├── build.sh # 编译脚本
├── my_ops.h # 算子声明 ├── CMakeLists.txt # CMake 配置文件
├── my_ops_impl.cpp # 算子实现 ├── _C.so # 编译后的共享库
├── my_ops.cpp # 模块初始化和算子定义 ├── setup.py # Python 包配置
└── test_ops.py # 测试脚本 ├── test/ # 测试目录
├── test_ops/ # 测试操作目录
├── test_ops.h # 算子声明
├── test_ops_impl.cpp # 算子实现
├── test_torch_library_expand_build_sh.py # 测试脚本
└── test_torch_library_expand.cpp # 模块初始化和算子定义
``` ```
## 3. 开发步骤 ## 3. 开发步骤
### 3.1 定义算子接口 ### 3.1 定义算子接口
使用 `TORCH_LIBRARY` 宏定义算子接口,在 `.cpp` 文件中: 使用 `TORCH_LIBRARY` 宏定义算子接口,在 `test_torch_library_expand.cpp` 文件中:
```cpp ```cpp
#include <torch/library.h> #include <torch/library.h>
#include <ATen/ATen.h> #include <ATen/ATen.h>
#include <Python.h> #include <Python.h>
#include "my_ops.h" #include <torch/all.h>
#include "test_ops.h"
// 定义算子库 #define TORCH_LIBRARY_EXPAND(NAME, MODULE) TORCH_LIBRARY(NAME, MODULE)
TORCH_LIBRARY(my_ops, m) { #define TORCH_HAS_CUDA
m.def("add_one(Tensor input) -> Tensor");
m.def("multiply_by_two(Tensor input) -> Tensor");
}
// Python 模块初始化函数 // Python模块初始化函数
extern "C" { extern "C" {
PyObject *PyInit_my_ops(void) {
PyObject *PyInit__C(void) {
static struct PyModuleDef module_def = { static struct PyModuleDef module_def = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT, "_C", /* name of module */
"my_ops", // 模块名 NULL, /* module documentation, may be NULL */
"My custom operations module", // 文档 -1, /* size of per-interpreter state of the module,
-1, or -1 if the module keeps state in global variables. */
NULL // 方法定义 NULL, /* methods */
}; };
return PyModule_Create(&module_def); return PyModule_Create(&module_def);
} }
} }
// 只在TORCH_LIBRARY中定义操作符,不实现
TORCH_LIBRARY_EXPAND(test_ops, ops) {
ops.def("add_one(Tensor input) -> Tensor");
ops.def("multiply_by_two(Tensor input) -> Tensor");
}
``` ```
### 3.2 实现算子逻辑 ### 3.2 实现算子逻辑
`.h` 文件中声明算子函数: `test_ops.h` 文件中声明算子函数:
```cpp ```cpp
#pragma once #pragma once
#include <torch/library.h> #include <torch/library.h>
#include <ATen/ATen.h> #include <ATen/ATen.h>
namespace my_ops_impl { namespace test_ops_impl {
at::Tensor add_one(at::Tensor input); at::Tensor add_one(at::Tensor input);
at::Tensor multiply_by_two(at::Tensor input); at::Tensor multiply_by_two(at::Tensor input);
} }
``` ```
`.cpp` 文件中实现算子逻辑并使用 `TORCH_LIBRARY_IMPL` 注册: `test_ops_impl.cpp` 文件中实现算子逻辑并使用 `TORCH_LIBRARY_IMPL` 注册:
```cpp ```cpp
#include <torch/library.h> #include <torch/library.h>
#include <ATen/ATen.h> #include <ATen/ATen.h>
#include "my_ops.h" #include "test_ops.h"
#define TORCH_LIBRARY_IMPL_EXPAND(NAME, DEVICE, MODULE) \
TORCH_LIBRARY_IMPL(NAME, DEVICE, MODULE)
#define TORCH_HAS_CUDA
namespace my_ops_impl { namespace test_ops_impl {
// 操作符的具体实现 // 操作符的具体实现
at::Tensor add_one(at::Tensor input) { at::Tensor add_one(at::Tensor input) {
return input + 1; return input + 1;
...@@ -96,17 +111,19 @@ namespace my_ops_impl { ...@@ -96,17 +111,19 @@ namespace my_ops_impl {
} }
} }
// 注册 CPU 实现 // 在TORCH_LIBRARY_IMPL中注册CPU实现
TORCH_LIBRARY_IMPL(my_ops, CPU, m) { TORCH_LIBRARY_IMPL_EXPAND(test_ops, CPU, cpu_ops) {
m.impl("add_one", &my_ops_impl::add_one); cpu_ops.impl("add_one", &test_ops_impl::add_one);
m.impl("multiply_by_two", &my_ops_impl::multiply_by_two); cpu_ops.impl("multiply_by_two", &test_ops_impl::multiply_by_two);
} }
// 注册 CUDA 实现(如果有 CUDA) // 在TORCH_LIBRARY_IMPL中注册CUDA实现(如果有CUDA)
#ifdef TORCH_HAS_CUDA #ifdef TORCH_HAS_CUDA
TORCH_LIBRARY_IMPL(my_ops, CUDA, m) { TORCH_LIBRARY_IMPL_EXPAND(test_ops, CUDA, cuda_ops) {
m.impl("add_one", &my_ops_impl::add_one); // 注意:这里假设CPU和CUDA使用相同的实现函数
m.impl("multiply_by_two", &my_ops_impl::multiply_by_two); // 如果CUDA需要不同的实现,可以定义专门的CUDA版本函数
cuda_ops.impl("add_one", &test_ops_impl::add_one);
cuda_ops.impl("multiply_by_two", &test_ops_impl::multiply_by_two);
} }
#endif #endif
``` ```
...@@ -119,15 +136,15 @@ TORCH_LIBRARY_IMPL(my_ops, CUDA, m) { ...@@ -119,15 +136,15 @@ TORCH_LIBRARY_IMPL(my_ops, CUDA, m) {
```cmake ```cmake
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.18)
project(my_ops) project(test_ops)
find_package(Torch REQUIRED) find_package(Torch REQUIRED)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(my_ops SHARED my_ops.cpp my_ops_impl.cpp) add_library(_C SHARED test_torch_library_expand.cpp test_ops_impl.cpp)
target_link_libraries(my_ops "${TORCH_LIBRARIES}") target_link_libraries(_C "${TORCH_LIBRARIES}")
set_target_properties(my_ops PROPERTIES PREFIX "") set_target_properties(_C PROPERTIES PREFIX "")
``` ```
#### 3.3.2 使用 setup.py 配置 #### 3.3.2 使用 setup.py 配置
...@@ -140,13 +157,13 @@ import torch ...@@ -140,13 +157,13 @@ import torch
from setuptools import setup, find_packages from setuptools import setup, find_packages
from torch.utils.cpp_extension import BuildExtension, CppExtension, CUDAExtension from torch.utils.cpp_extension import BuildExtension, CppExtension, CUDAExtension
library_name = "my_ops" library_name = "test_ops"
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
# 源文件列表 # 源文件列表
sources = [ sources = [
os.path.join(current_dir, "my_ops.cpp"), os.path.join(current_dir, "test_torch_library_expand.cpp"),
os.path.join(current_dir, "my_ops_impl.cpp"), os.path.join(current_dir, "test_ops_impl.cpp"),
] ]
# 检查CUDA是否可用 # 检查CUDA是否可用
...@@ -180,8 +197,8 @@ __all__ = ['add_one', 'multiply_by_two'] ...@@ -180,8 +197,8 @@ __all__ = ['add_one', 'multiply_by_two']
setup( setup(
name=library_name, name=library_name,
version='0.1.0', version='0.1.1',
description='Custom operations for PyTorch', description='Test operations for PyTorch',
author='Your Name', author='Your Name',
# 关键:指定包 # 关键:指定包
...@@ -208,6 +225,12 @@ setup( ...@@ -208,6 +225,12 @@ setup(
# 确保生成正确的 .dist-info # 确保生成正确的 .dist-info
zip_safe=False, zip_safe=False,
# 添加以下参数来避免生成 .egg-info 在当前目录
options={
'egg_info': {
'egg_base': '/tmp' # 将 egg-info 生成到临时目录
}
},
) )
``` ```
...@@ -217,24 +240,52 @@ setup( ...@@ -217,24 +240,52 @@ setup(
```bash ```bash
#!/bin/bash #!/bin/bash
rm -rf build
mkdir -p build # 构建目录
cd build BUILD_DIR="./build"
# 创建构建目录
mkdir -p $BUILD_DIR
cd $BUILD_DIR
# 运行 CMake
cmake .. cmake ..
make -j
cp my_ops.so .. # 编译
cmake --build .
# 复制编译后的库到当前目录
cp *.so ..
cd .. cd ..
# 运行测试
python test_torch_library_expand_build_sh.py
``` ```
## 4. 编译和安装 ## 4. 编译方法对比
### 4.1 两种编译方法的区别
| 特性 | CMake + build.sh | setup.py |
|------|-----------------|----------|
| **编译方式** | 手动调用 CMake 和编译命令 | 使用 Python setuptools 自动编译 |
| **依赖管理** | 需要手动安装依赖 | 自动处理依赖关系 |
| **安装方式** | 仅编译,不安装到 Python 环境 | 可以安装到 Python 环境中 |
| **CUDA 支持** | 需要手动配置 | 自动检测并配置 CUDA |
| **输出文件** | 直接生成 .so 文件 | 生成 Python 包结构 |
| **灵活性** | 更灵活,可以自定义编译流程 | 更简单,适合快速开发 |
| **使用场景** | 适合复杂项目,需要精细控制编译过程 | 适合简单项目,快速集成到 Python 环境 |
### 4.1 使用 build.sh 编译 ### 4.2 编译和安装
#### 4.2.1 使用 build.sh 编译
```bash ```bash
bash build.sh bash build.sh
``` ```
### 4.2 使用 pip 安装 #### 4.2.2 使用 pip 安装
```bash ```bash
pip install --no-build-isolation . pip install --no-build-isolation .
...@@ -242,7 +293,7 @@ pip install --no-build-isolation . ...@@ -242,7 +293,7 @@ pip install --no-build-isolation .
## 5. 在 Python 中使用自定义算子 ## 5. 在 Python 中使用自定义算子
创建测试脚本 `test_ops.py` 创建测试脚本 `test_torch_library_expand_build_sh.py`
```python ```python
import torch import torch
...@@ -254,8 +305,9 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) ...@@ -254,8 +305,9 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 加载自定义操作符 # 加载自定义操作符
try: try:
import my_ops # 直接加载共享库
print("成功加载 my_ops 扩展") import _C
print("成功加载 _C 扩展")
except ImportError as e: except ImportError as e:
print(f"加载扩展失败: {e}") print(f"加载扩展失败: {e}")
print("请先编译 C++ 扩展") print("请先编译 C++ 扩展")
...@@ -270,7 +322,7 @@ def test_add_one(): ...@@ -270,7 +322,7 @@ def test_add_one():
print(f"输入: {x}") print(f"输入: {x}")
# 调用自定义操作符 # 调用自定义操作符
y = torch.ops.my_ops.add_one(x) y = torch.ops.test_ops.add_one(x)
print(f"输出: {y}") print(f"输出: {y}")
# 验证结果 # 验证结果
...@@ -287,7 +339,7 @@ def test_multiply_by_two(): ...@@ -287,7 +339,7 @@ def test_multiply_by_two():
print(f"输入: {x}") print(f"输入: {x}")
# 调用自定义操作符 # 调用自定义操作符
y = torch.ops.my_ops.multiply_by_two(x) y = torch.ops.test_ops.multiply_by_two(x)
print(f"输出: {y}") print(f"输出: {y}")
# 验证结果 # 验证结果
...@@ -305,12 +357,12 @@ def test_cuda_support(): ...@@ -305,12 +357,12 @@ def test_cuda_support():
print(f"CUDA 输入: {x}") print(f"CUDA 输入: {x}")
# 测试 add_one # 测试 add_one
y = torch.ops.my_ops.add_one(x) y = torch.ops.test_ops.add_one(x)
print(f"add_one 输出: {y}") print(f"add_one 输出: {y}")
assert torch.allclose(y, x + 1), f"CUDA add_one 结果不匹配" assert torch.allclose(y, x + 1), f"CUDA add_one 结果不匹配"
# 测试 multiply_by_two # 测试 multiply_by_two
z = torch.ops.my_ops.multiply_by_two(x) z = torch.ops.test_ops.multiply_by_two(x)
print(f"multiply_by_two 输出: {z}") print(f"multiply_by_two 输出: {z}")
assert torch.allclose(z, x * 2), f"CUDA multiply_by_two 结果不匹配" assert torch.allclose(z, x * 2), f"CUDA multiply_by_two 结果不匹配"
...@@ -333,7 +385,7 @@ if __name__ == "__main__": ...@@ -333,7 +385,7 @@ if __name__ == "__main__":
运行测试: 运行测试:
```bash ```bash
python test_ops.py python test_torch_library_expand_build_sh.py
``` ```
## 6. 常见问题和解决方案 ## 6. 常见问题和解决方案
...@@ -342,11 +394,11 @@ python test_ops.py ...@@ -342,11 +394,11 @@ python test_ops.py
**错误信息** **错误信息**
``` ```
加载扩展失败: dynamic module does not define module export function (PyInit_my_ops) 加载扩展失败: dynamic module does not define module export function (PyInit__C)
``` ```
**解决方案** **解决方案**
确保在 C++ 文件中正确定义了 `PyInit_my_ops` 函数,并且函数名与模块名一致。 确保在 C++ 文件中正确定义了 `PyInit__C` 函数,并且函数名与模块名一致。在本项目中,模块名是 `_C`,所以初始化函数名必须是 `PyInit__C`
### 6.2 CUDA 支持问题 ### 6.2 CUDA 支持问题
...@@ -356,7 +408,7 @@ undefined reference to `torch::library::Library::impl(...)` ...@@ -356,7 +408,7 @@ undefined reference to `torch::library::Library::impl(...)`
``` ```
**解决方案** **解决方案**
确保正确定义了 `TORCH_HAS_CUDA` 宏,并且在 CUDA 环境下编译。 确保正确定义了 `TORCH_HAS_CUDA` 宏,并且在 CUDA 环境下编译。在本项目中,已经定义了 `TORCH_HAS_CUDA` 宏。
### 6.3 算子注册问题 ### 6.3 算子注册问题
...@@ -376,14 +428,14 @@ undefined symbol: _ZN5torch8library7Library4implERKNS_6SymbolEPFSt10unique_ptrIN ...@@ -376,14 +428,14 @@ undefined symbol: _ZN5torch8library7Library4implERKNS_6SymbolEPFSt10unique_ptrIN
```cpp ```cpp
// CPU 实现 // CPU 实现
TORCH_LIBRARY_IMPL(my_ops, CPU, m) { TORCH_LIBRARY_IMPL(test_ops, CPU, m) {
m.impl("add_one", &my_ops_impl::add_one_cpu); m.impl("add_one", &test_ops_impl::add_one_cpu);
} }
// CUDA 实现 // CUDA 实现
#ifdef TORCH_HAS_CUDA #ifdef TORCH_HAS_CUDA
TORCH_LIBRARY_IMPL(my_ops, CUDA, m) { TORCH_LIBRARY_IMPL(test_ops, CUDA, m) {
m.impl("add_one", &my_ops_impl::add_one_cuda); m.impl("add_one", &test_ops_impl::add_one_cuda);
} }
#endif #endif
``` ```
...@@ -393,7 +445,7 @@ TORCH_LIBRARY_IMPL(my_ops, CUDA, m) { ...@@ -393,7 +445,7 @@ TORCH_LIBRARY_IMPL(my_ops, CUDA, m) {
可以定义更复杂的算子,支持多个输入和输出: 可以定义更复杂的算子,支持多个输入和输出:
```cpp ```cpp
TORCH_LIBRARY(my_ops, m) { TORCH_LIBRARY(test_ops, m) {
m.def("add_scalar(Tensor input, Scalar scalar) -> Tensor"); m.def("add_scalar(Tensor input, Scalar scalar) -> Tensor");
m.def("addmm(Tensor self, Tensor mat1, Tensor mat2, Scalar beta=1, Scalar alpha=1) -> Tensor"); m.def("addmm(Tensor self, Tensor mat1, Tensor mat2, Scalar beta=1, Scalar alpha=1) -> Tensor");
} }
...@@ -404,7 +456,7 @@ TORCH_LIBRARY(my_ops, m) { ...@@ -404,7 +456,7 @@ TORCH_LIBRARY(my_ops, m) {
可以为不同类型的输入提供重载: 可以为不同类型的输入提供重载:
```cpp ```cpp
TORCH_LIBRARY(my_ops, m) { TORCH_LIBRARY(test_ops, m) {
m.def("add(Tensor a, Tensor b) -> Tensor"); m.def("add(Tensor a, Tensor b) -> Tensor");
m.def("add(Tensor a, Scalar b) -> Tensor"); m.def("add(Tensor a, Scalar b) -> Tensor");
} }
...@@ -419,15 +471,20 @@ TORCH_LIBRARY(my_ops, m) { ...@@ -419,15 +471,20 @@ TORCH_LIBRARY(my_ops, m) {
3. **无缝集成到 PyTorch**:通过 `PyInit_【NAME】` 函数初始化 Python 模块 3. **无缝集成到 PyTorch**:通过 `PyInit_【NAME】` 函数初始化 Python 模块
4. **灵活编译和安装**:支持使用 CMake 或 setup.py 编译和安装 4. **灵活编译和安装**:支持使用 CMake 或 setup.py 编译和安装
两种编译方法各有优缺点:
- **CMake + build.sh**:更灵活,适合复杂项目,需要精细控制编译过程
- **setup.py**:更简单,适合快速开发,可以自动处理依赖关系并安装到 Python 环境
这种方法为 PyTorch 自定义算子开发提供了一种简洁、灵活的方式,使得我们可以轻松扩展 PyTorch 的功能,满足特定的业务需求。 这种方法为 PyTorch 自定义算子开发提供了一种简洁、灵活的方式,使得我们可以轻松扩展 PyTorch 的功能,满足特定的业务需求。
## 9. 代码示例 ## 9. 代码示例
完整的代码示例可以参考 `torch_custom_op` 工程中的各个部分 完整的代码示例可以参考 `torch_custom_op` 工程中的 `torch_library_custom_op/custom_op` 目录
- **1part**:展示了在单个文件中同时定义和实现算子 - **test_torch_library_expand.cpp**:展示了如何使用 `TORCH_LIBRARY` 定义算子接口和初始化 Python 模块
- **2part**:展示了将定义和实现分离到不同文件 - **test_ops_impl.cpp**:展示了如何实现算子逻辑并使用 `TORCH_LIBRARY_IMPL` 注册到不同设备
- **3part**:展示了如何打包为可安装的 Python 包 - **CMakeLists.txt**:展示了如何使用 CMake 配置编译
- **4part**:展示了不同的模块初始化方式 - **setup.py**:展示了如何使用 setup.py 配置编译和安装
- **build.sh**:展示了如何使用脚本编译和测试
通过这些示例,您可以快速上手 PyTorch 自定义算子的开发和接入。 通过这些示例,您可以快速上手 PyTorch 自定义算子的开发和接入。
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment