已合并
fix: torch ops cpp sources #8489
丛吉钰创建于 17 天前
fix: torch ops cpp sources #8489
已合并
丛吉钰创建于 17 天前
15 个文件变更+114-87
Mactivation/swiglu_group/torch_extension/swiglu_group.py+1-1
@@ -62,7 +62,7 @@ class SwigluGroupOpBuilder(OpBuilder):
62 super().__init__("swiglu_group")62 super().__init__("swiglu_group")
63 63 
64 def sources(self):64 def sources(self):
65- return ["csrc/activation/swiglu_group.cpp"]65+ return [self.resolve_source("swiglu_group.cpp")]
66 66 
67 def schema(self):67 def schema(self):
68 return (68 return (
Mactivation/swiglu_group_quant/torch_extension/swiglu_group_quant.py+1-1
@@ -143,7 +143,7 @@ class SwigluGroupQuantOpBuilder(OpBuilder):
143 super().__init__("swiglu_group_quant")143 super().__init__("swiglu_group_quant")
144 144 
145 def sources(self):145 def sources(self):
146- return ["csrc/activation/swiglu_group_quant.cpp"]146+ return [self.resolve_source("swiglu_group_quant.cpp")]
147 147 
148 def schema(self):148 def schema(self):
149 return (149 return (
Mactivation/swiglu_group_quant_grad/torch_extension/swiglu_group_quant_grad.py+1-1
@@ -19,7 +19,7 @@ class SwigluGroupQuantBackwardOpBuilder(OpBuilder):
19 super().__init__("swiglu_group_quant_backward")19 super().__init__("swiglu_group_quant_backward")
20 20 
21 def sources(self):21 def sources(self):
22- return ["csrc/activation/swiglu_group_quant_grad.cpp"]22+ return [self.resolve_source("swiglu_group_quant_grad.cpp")]
23 23 
24 def schema(self):24 def schema(self):
25 return (25 return (
Mexperimental/activation/swiglu_clamp/torch_extension/swiglu_clamp.py+3-3
@@ -9,7 +9,7 @@
9# -----------------------------------------------------------------------------------------------------------9# -----------------------------------------------------------------------------------------------------------
10 10 
11# SwigluClamp PTA Python 前端11# SwigluClamp PTA Python 前端
12-# 管理 JIT 编译(csrc/activation/swiglu_clamp.cpp)并把算子注册到 PyTorch Dispatcher。12+# 管理 JIT 编译(csrc/swiglu_clamp.cpp)并把算子注册到 PyTorch Dispatcher。
13# 注册后可通过 torch.ops.cann_ops_nn.swiglu_clamp(x, limit) 调用。13# 注册后可通过 torch.ops.cann_ops_nn.swiglu_clamp(x, limit) 调用。
14# 参考: torch_extension/README.md「新增算子」+ PR !5910 (commit d8af5e890)。14# 参考: torch_extension/README.md「新增算子」+ PR !5910 (commit d8af5e890)。
15 15 
@@ -24,7 +24,7 @@ class SwigluClampOpBuilder(OpBuilder):
24 24 
25 def sources(self):25 def sources(self):
26 """C++ 源码路径(相对 cann_ops_nn 包根)。"""26 """C++ 源码路径(相对 cann_ops_nn 包根)。"""
27- return ["csrc/activation/swiglu_clamp.cpp"]27+ return [self.resolve_source("swiglu_clamp.cpp")]
28 28 
29 def schema(self) -> str:29 def schema(self) -> str:
30 """PyTorch 算子签名。limit 默认 7.0(Step-3.7 专家路)。"""30 """PyTorch 算子签名。limit 默认 7.0(Step-3.7 专家路)。"""
@@ -51,5 +51,5 @@ builder._ensure_initialized()
51@impl(get_as_library(), "swiglu_clamp", "PrivateUse1")51@impl(get_as_library(), "swiglu_clamp", "PrivateUse1")
52def swiglu_clamp(x: torch.Tensor, limit: float = 7.0):52def swiglu_clamp(x: torch.Tensor, limit: float = 7.0):
53 """Dispatcher 的 NPU 实现。PrivateUse1 是 NPU 后端分发键。"""53 """Dispatcher 的 NPU 实现。PrivateUse1 是 NPU 后端分发键。"""
54- op_module = builder.load() # JIT 编译/加载 csrc/activation/swiglu_clamp.cpp54+ op_module = builder.load() # JIT 编译/加载 csrc/swiglu_clamp.cpp
55 return op_module.swiglu_clamp(x, limit)55 return op_module.swiglu_clamp(x, limit)
Mmatmul/matmul_emu_split_weight/torch_extension/matmul_emu_split_weight.py+1-1
@@ -21,7 +21,7 @@ class MatmulEmuSplitWeightOpBuilder(OpBuilder):
21 """21 """
22 22 
23 def __init__(self):23 def __init__(self):
24- super().__init__("matmul_emu_split_weight", category="matmul")24+ super().__init__("matmul_emu_split_weight")
25 25 
26 def sources(self) -> list:26 def sources(self) -> list:
27 return [self.resolve_source("matmul_emu_split_weight.cpp")]27 return [self.resolve_source("matmul_emu_split_weight.cpp")]
Mmatmul/quant_matmul_activation_quant/torch_extension/quant_matmul_activation_quant.py+1-1
@@ -24,7 +24,7 @@ class QuantMatmulActivationQuantOpBuilder(OpBuilder):
24 super().__init__("quant_matmul_activation_quant")24 super().__init__("quant_matmul_activation_quant")
25 25 
26 def sources(self) -> list:26 def sources(self) -> list:
27- return ["csrc/matmul/quant_matmul_activation_quant.cpp"]27+ return [self.resolve_source("quant_matmul_activation_quant.cpp")]
28 28 
29 def schema(self) -> str:29 def schema(self) -> str:
30 return (30 return (
Mnorm/rms_norm_dynamic_quant/torch_extension/rms_norm_dynamic_quant.py+1-1
@@ -27,7 +27,7 @@ class RmsNormDynamicQuantOpBuilder(OpBuilder):
27 super().__init__("rms_norm_dynamic_quant")27 super().__init__("rms_norm_dynamic_quant")
28 28 
29 def sources(self) -> list:29 def sources(self) -> list:
30- return ["csrc/norm/rms_norm_dynamic_quant.cpp"]30+ return [self.resolve_source("rms_norm_dynamic_quant.cpp")]
31 31 
32 def schema(self) -> str:32 def schema(self) -> str:
33 return (33 return (
Mquant/flat_quant/torch_extension/flat_quant.py+2-2
@@ -24,10 +24,10 @@ class FlatQuantOpBuilder(OpBuilder):
24 """24 """
25 25 
26 def __init__(self):26 def __init__(self):
27- super().__init__("flat_quant", "quant")27+ super().__init__("flat_quant")
28 28 
29 def sources(self) -> list:29 def sources(self) -> list:
30- return ["csrc/quant/flat_quant.cpp"]30+ return [self.resolve_source("flat_quant.cpp")]
31 31 
32 def schema(self) -> str:32 def schema(self) -> str:
33 return (33 return (
Mquant/grouped_dynamic_mx_quant_with_dual_axis/torch_extension/grouped_dynamic_mx_quant_with_dual_axis.py+1-1
@@ -21,7 +21,7 @@ class GroupedDynamicMxQuantWithDualAxisOpBuilder(OpBuilder):
21 """21 """
22 22 
23 def __init__(self):23 def __init__(self):
24- super().__init__("grouped_dynamic_mx_quant_with_dual_axis", category="quant")24+ super().__init__("grouped_dynamic_mx_quant_with_dual_axis")
25 25 
26 def sources(self) -> list:26 def sources(self) -> list:
27 return [self.resolve_source("grouped_dynamic_mx_quant_with_dual_axis.cpp")]27 return [self.resolve_source("grouped_dynamic_mx_quant_with_dual_axis.cpp")]
Mquant/mx_to_block_mx_quant/torch_extension/mx_to_block_mx_quant.py+1-1
@@ -50,7 +50,7 @@ class MxToBlockMxQuantOpBuilder(OpBuilder):
50 super().__init__("mx_to_block_mx_quant")50 super().__init__("mx_to_block_mx_quant")
51 51 
52 def sources(self) -> list:52 def sources(self) -> list:
53- return ["csrc/quant/mx_to_block_mx_quant.cpp"]53+ return [self.resolve_source("mx_to_block_mx_quant.cpp")]
54 54 
55 def schema(self) -> str:55 def schema(self) -> str:
56 return (56 return (
Mquant/swiglu_group_grad/torch_extension/swiglu_group_grad.py+1-1
@@ -92,7 +92,7 @@ def _check_swiglu_group_backward_inputs(
92 92 
93class SwigluGroupBackwardOpBuilder(OpBuilder):93class SwigluGroupBackwardOpBuilder(OpBuilder):
94 def __init__(self):94 def __init__(self):
95- super().__init__("swiglu_group_backward", category="quant")95+ super().__init__("swiglu_group_backward")
96 96 
97 def sources(self):97 def sources(self):
98 return [self.resolve_source("swiglu_group_grad.cpp")]98 return [self.resolve_source("swiglu_group_grad.cpp")]
Mtorch_extension/README.md+31-30
@@ -69,10 +69,10 @@ TORCH_EXTENSION_OPS=swiglu_group TORCH_EXTENSION_VENDOR=custom python3 -m build
69 69 
70```sh70```sh
71# 安装整包71# 安装整包
72-python3 -m pip install dist/cann_ops_nn-*.whl --no-deps72+python3 -m pip install dist/cann_ops_nn-*.whl
73 73 
74# 安装单算子包74# 安装单算子包
75-python3 -m pip install dist/cann_ops_nn_custom-*.whl --no-deps75+python3 -m pip install dist/cann_ops_nn_custom-*.whl
76```76```
77 77 
78### 整包与子包共存机制78### 整包与子包共存机制
@@ -86,10 +86,10 @@ python3 -m pip install dist/cann_ops_nn_custom-*.whl --no-deps
86 86 
87```sh87```sh
88# 安装整包88# 安装整包
89-pip install cann_ops_nn-*.whl --no-deps89+pip install cann_ops_nn-*.whl
90 90 
91# 安装单算子包(覆盖整包中的同名算子)91# 安装单算子包(覆盖整包中的同名算子)
92-pip install cann_ops_nn_custom-*.whl --no-deps92+pip install cann_ops_nn_custom-*.whl
93 93 
94# 卸载单算子包(整包算子自动恢复)94# 卸载单算子包(整包算子自动恢复)
95pip uninstall cann-ops-nn-custom95pip uninstall cann-ops-nn-custom
@@ -100,29 +100,30 @@ pip uninstall cann-ops-nn-custom
100```100```
101├── torch_extension101├── torch_extension
102│ ├── build.sh # 构建脚本(支持 --torch_extension 参数)102│ ├── build.sh # 构建脚本(支持 --torch_extension 参数)
103-│ ├── setup.py # wheel 打包配置(支持单算子编包103+│ ├── setup.py # wheel 打包配置(自动收集分布式算子)
104│ ├── requirements.txt104│ ├── requirements.txt
105-── cann_ops_nn # 整包安装目录105+── cann_ops_nn # 整包安装目录(框架公共部分)
106-├── __init__.py # 包入口,from .ops import *106+ ├── __init__.py # 包入口,from .ops import *
107-├── op_builder/107+ ├── op_builder/
108- └── builder.py # OpBuilder 基类,JIT 编译管理108+ │ └── builder.py # OpBuilder 基类,JIT 编译管理
109-├── common/109+ ├── common/
110- └── aclnn_common.h # ACLNN_CMD 宏等公共能力110+ │ └── aclnn_common.h # ACLNN_CMD 宏等公共能力
111-├── csrc/111+ ├── csrc/
112- └── <category>/<op>.cpp # C++ kernel wrapper112+ │ └── extension.cpp # 占位模块(满足 PyTorch 扩展要求)
113-├── ops/113+ ├── ops/
114- │ ├── __init__.py # 自动发现算子 + entry point 加载114+ ── __init__.py # 自动发现算子 + entry point 加载(构建时填充)
115- │ │ └── <category>/<op>/<op>.py # Python 前端115+ └── docs/
116-└── docs/116+ └── torch_extension_guidelines.md # 开发规范
117-│ │ └── torch_extension_guidelines.md # 开发规范117+── <category>/<op>/torch_extension/ # 算子开发目录(仓库根目录下)
118-── dist/ # 构建产物输出目录118+── <op>.py # Python 前端(OpBuilder + schema + meta)
119-├── <category>/<op>/torch_extension/ # 分布式算子(仓库根目录下)
120-│ ├── <op>.py
121│ ├── __init__.py119│ ├── __init__.py
122-── csrc/<op>.cpp120+── graph_convert_<op>.py # (可选)图回退转换
121+│ └── csrc/<op>.cpp # C++ kernel wrapper
123└── build_out/ # build.sh 输出目录122└── build_out/ # build.sh 输出目录
124```123```
125 124 
125+> **构建时收集:** `setup.py` 在打包阶段自动遍历仓库中所有 `<category>/<op>/torch_extension/`,把 `<op>.py`、`csrc/<op>.cpp` 汇集到 wheel 包目录 `cann_ops_nn/ops/<category>/<op>/` 与 `cann_ops_nn/csrc/` 下。开发者无需手动在 `cann_ops_nn/` 下创建算子文件。
126+ 
126## 快速入门127## 快速入门
127 128 
128```python129```python
@@ -144,19 +145,19 @@ print(result.shape) # torch.Size([16, 16])
144 145 
145## 开发者指南:新增算子146## 开发者指南:新增算子
146 147 
147-以添加新算子 `new_operator` 为例,需提供 C++ 绑定和 Python 构建器。148+以添加新算子 `new_operator`(类别 `my_cat`)为例,在算子目录 `<my_cat>/new_operator/` 下创建 `torch_extension/` 子目录,包含 C++ 绑定和 Python 构建器。
148 149 
149-### 1. C++ 后端 (`cann_ops_nn/csrc/<op_category>/new_operator.cpp`)150+### 1. C++ 后端 (`<my_cat>/new_operator/torch_extension/csrc/new_operator.cpp`)
150 151 
151该文件将 PyTorch 张量桥接到 ACLNN C-API。152该文件将 PyTorch 张量桥接到 ACLNN C-API。
152 153 
153```cpp154```cpp
154#include <torch/extension.h>155#include <torch/extension.h>
155#include "aclnnop/aclnn_new_operator.h"156#include "aclnnop/aclnn_new_operator.h"
156-#include "../common/aclnn_common.h"157+#include "aclnn_common.h" // 通过 -I cann_ops_nn/common 解析
157 158 
158namespace cann_ops_nn {159namespace cann_ops_nn {
159-namespace <op_category> {160+namespace my_cat {
160 161 
161at::Tensor new_operator(162at::Tensor new_operator(
162 const at::Tensor& input1,163 const at::Tensor& input1,
@@ -178,16 +179,16 @@ at::Tensor new_operator(
178 return out;179 return out;
179}180}
180 181 
181-} // namespace <op_category>182+} // namespace my_cat
182} // namespace cann_ops_nn183} // namespace cann_ops_nn
183 184 
184PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {185PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
185- m.def("new_operator", &cann_ops_nn::<op_category>::new_operator,186+ m.def("new_operator", &cann_ops_nn::my_cat::new_operator,
186 "NewOperator on NPU");187 "NewOperator on NPU");
187}188}
188```189```
189 190 
190-### 2. Python 前端 (`cann_ops_nn/ops/<op_category>/new_operator.py`)191+### 2. Python 前端 (`<my_cat>/new_operator/torch_extension/new_operator.py`)
191 192 
192该文件管理 JIT 编译逻辑并将算子注册到 PyTorch Dispatcher。193该文件管理 JIT 编译逻辑并将算子注册到 PyTorch Dispatcher。
193 194 
@@ -203,7 +204,7 @@ class NewOperatorOpBuilder(OpBuilder):
203 204 
204 def sources(self):205 def sources(self):
205 """C++ 源码路径。"""206 """C++ 源码路径。"""
206- return ['csrc/<op_category>/new_operator.cpp']207+ return [self.resolve_source("new_operator.cpp")]
207 208 
208 def schema(self) -> str:209 def schema(self) -> str:
209 """PyTorch 算子签名。"""210 """PyTorch 算子签名。"""
Mtorch_extension/cann_ops_nn/docs/torch_extension_guidelines.md+4-5
@@ -25,8 +25,7 @@ nn仓的算子采用**分布式目录结构**:每个算子的torch_extension
25│ │ ├── common/25│ │ ├── common/
26│ │ │ └── aclnn_common.h # ACLNN_CMD宏、类型转换等公共能力(一般无需修改)26│ │ │ └── aclnn_common.h # ACLNN_CMD宏、类型转换等公共能力(一般无需修改)
27│ │ ├── csrc/ # C++源码staging目录(构建时自动收集)27│ │ ├── csrc/ # C++源码staging目录(构建时自动收集)
28-│ │ │ └── ${category}/28+│ │ │ └── ${op_api}.cpp
29-│ │ │ └── ${op_api}.cpp
30│ │ └── ops/29│ │ └── ops/
31│ │ ├── __init__.py # 算子自动发现与加载30│ │ ├── __init__.py # 算子自动发现与加载
32│ │ └── ${category}/31│ │ └── ${category}/
@@ -200,7 +199,7 @@ nn仓的算子采用**分布式目录结构**:每个算子的torch_extension
200负责JIT编译管理、schema/meta注册与对外接口封装:199负责JIT编译管理、schema/meta注册与对外接口封装:
201 200 
2021. **OpBuilder子类**:继承`OpBuilder`,在`__init__`中以`super().__init__("<schema算子名>")`传入算子名,并实现三个抽象方法:2011. **OpBuilder子类**:继承`OpBuilder`,在`__init__`中以`super().__init__("<schema算子名>")`传入算子名,并实现三个抽象方法:
203- - `sources()`:返回相对`cann_ops_nn`包根的C++源文件路径列表,如`["csrc/activation/swiglu_group.cpp"]`;202+ - `sources()`:返回相对`cann_ops_nn`包根的C++源文件路径列表,统一通过`self.resolve_source("${op_api}.cpp")`获取(如`[self.resolve_source("swiglu_group.cpp")]`
204 - `schema()`:返回算子schema字符串(见[2.3 Schema标识符](#schema标识符算子签名));203 - `schema()`:返回算子schema字符串(见[2.3 Schema标识符](#schema标识符算子签名));
205 - `register_meta()`:用`@impl(get_as_library(), self.name, "Meta")`注册Meta实现,仅做shape/dtype推导,不触碰真实NPU计算(FakeTensor/图模式必需)。204 - `register_meta()`:用`@impl(get_as_library(), self.name, "Meta")`注册Meta实现,仅做shape/dtype推导,不触碰真实NPU计算(FakeTensor/图模式必需)。
2062. **实例化与初始化**:模块加载时实例化builder并调用`_ensure_initialized()`注册schema与meta:2052. **实例化与初始化**:模块加载时实例化builder并调用`_ensure_initialized()`注册schema与meta:
@@ -225,8 +224,8 @@ nn仓的算子采用**分布式目录结构**:每个算子的torch_extension
225 def __init__(self):224 def __init__(self):
226 super().__init__("swiglu_group")225 super().__init__("swiglu_group")
227 226 
228- def sources(self):227+ def sources(self):
229- return ["csrc/activation/swiglu_group.cpp"]228+ return [self.resolve_source("swiglu_group.cpp")]
230 229 
231 def schema(self):230 def schema(self):
232 return "swiglu_group(Tensor x, *, Tensor? weight=None, Tensor? group_index=None, float clamp_limit=-1.0) -> Tensor"231 return "swiglu_group(Tensor x, *, Tensor? weight=None, Tensor? group_index=None, float clamp_limit=-1.0) -> Tensor"
Mtorch_extension/cann_ops_nn/op_builder/builder.py+27-15
@@ -9,6 +9,7 @@
9# -----------------------------------------------------------------------------------------------------------9# -----------------------------------------------------------------------------------------------------------
10 10 
11import os11import os
12+import shutil
12from abc import ABC, abstractmethod13from abc import ABC, abstractmethod
13from typing import List, Union14from typing import List, Union
14import torch15import torch
@@ -34,14 +35,12 @@ class OpBuilder(ABC):
34 基于 aclnn 的算子构建基类35 基于 aclnn 的算子构建基类
35 36 
36 :param name: 算子名称,如 'mat_mul_v3'37 :param name: 算子名称,如 'mat_mul_v3'
37- :param category: 算子类别目录名,如 'matmul'。用于解析源码路径:csrc/<category>/<name>.cpp
38 """38 """
39 39 
40 _loaded_ops = {}40 _loaded_ops = {}
41 41 
42- def __init__(self, name, category=None):42+ def __init__(self, name):
43 self.name = name43 self.name = name
44- self.category = category
45 self._initialized = False44 self._initialized = False
46 45 
47 def _ensure_initialized(self):46 def _ensure_initialized(self):
@@ -79,10 +78,7 @@ class OpBuilder(ABC):
79 return [os.path.join(self._package_path, path) for path in paths]78 return [os.path.join(self._package_path, path) for path in paths]
80 79 
81 def resolve_source(self, cpp_filename):80 def resolve_source(self, cpp_filename):
82- self._ensure_initialized()81+ return f"csrc/{cpp_filename}"
范其瑞
范其瑞范其瑞17 天前

请尽量保证包里的目录结构层次和仓里的一致

likedislike
丛吉钰
丛吉钰
17 天前 评论:
83- if self.category is None:
84- return cpp_filename
85- return f"csrc/{self.category}/{cpp_filename}"
86 82 
87 def register_schema(self, op_schema: Union[str, List[str]]):83 def register_schema(self, op_schema: Union[str, List[str]]):
88 if isinstance(op_schema, str):84 if isinstance(op_schema, str):
@@ -175,14 +171,30 @@ class OpBuilder(ABC):
175 if self.name in OpBuilder._loaded_ops:171 if self.name in OpBuilder._loaded_ops:
176 return OpBuilder._loaded_ops[self.name]172 return OpBuilder._loaded_ops[self.name]
177 173 
178- op_module = load(174+ if shutil.which("ninja") is None:
179- name=self.name,175+ raise RuntimeError(
180- sources=self.get_absolute_paths(self.sources()),176+ f"ninja is required to JIT compile operator '{self.name}', "
181- extra_include_paths=self.get_absolute_paths(self.include_paths()),177+ f"please install it via 'pip install ninja'"
182- extra_cflags=self.cxx_args(),178+ )
183- extra_ldflags=self.extra_ldflags(),179+ 
184- verbose=verbose,180+ try:
185- )181+ op_module = load(
182+ name=self.name,
183+ sources=self.get_absolute_paths(self.sources()),
184+ extra_include_paths=self.get_absolute_paths(self.include_paths()),
185+ extra_cflags=self.cxx_args(),
186+ extra_ldflags=self.extra_ldflags(),
187+ verbose=verbose,
188+ )
189+ except Exception as e:
190+ raise RuntimeError(
191+ f"Failed to JIT compile operator '{self.name}': {e}\n"
192+ f"Common causes:\n"
193+ f" 1. CANN toolkit not sourced: source <cann_path>/set_env.sh\n"
194+ f" 2. Missing compiler: ensure gcc/g++ in PATH\n"
195+ f" 3. Missing ninja: pip install ninja"
196+ ) from e
197+ 
186 OpBuilder._loaded_ops[self.name] = op_module198 OpBuilder._loaded_ops[self.name] = op_module
187 199 
188 return op_module200 return op_module
Mtorch_extension/setup.py+38-23
@@ -76,14 +76,14 @@ def _non_python_files(directory):
76 if _selected_ops is not None:76 if _selected_ops is not None:
77 rel = os.path.relpath(path, directory)77 rel = os.path.relpath(path, directory)
78 parts = rel.split(os.sep)78 parts = rel.split(os.sep)
79- if (79+ if len(parts) >= 2 and parts[0] == "ops" and parts[1] not in _selected_ops:
80- len(parts) >= 2
81- and parts[0] in ("ops", "csrc")
82- and parts[1] not in _selected_ops
83- ):
84 continue80 continue
85 for filename in filenames:81 for filename in filenames:
86 if filename.endswith((".h", ".cpp")):82 if filename.endswith((".h", ".cpp")):
83+ if _selected_ops is not None and filename.endswith(".cpp"):
84+ op_name = filename[:-4]
85+ if op_name not in _selected_ops:
86+ continue
87 paths.append(os.path.join(path, filename))87 paths.append(os.path.join(path, filename))
88 return paths88 return paths
89 89 
@@ -103,6 +103,23 @@ _op_py_files = []
103_op_cpp_files = []103_op_cpp_files = []
104 104 
105 105 
106+def _collect_cpp(csrc_dir):
107+ if not os.path.isdir(csrc_dir):
108+ return
109+ for cpp in os.listdir(csrc_dir):
110+ if not cpp.endswith(".cpp"):
111+ continue
112+ dst_rel = os.path.join("csrc", cpp)
113+ src_abs = os.path.join(csrc_dir, cpp)
114+ for existing_rel, existing_src in _op_cpp_files:
115+ if existing_rel == dst_rel:
116+ raise ValueError(
117+ "Duplicate csrc file '%s' from '%s' conflicts with "
118+ "already collected '%s'" % (dst_rel, src_abs, existing_src)
119+ )
120+ _op_cpp_files.append((dst_rel, src_abs))
121+ 
122+ 
106def _collect_op(cat, name, torch_extension):123def _collect_op(cat, name, torch_extension):
107 # Collect one operator's torch_extension dir, staging its files under (cat, name).124 # Collect one operator's torch_extension dir, staging its files under (cat, name).
108 _selected_op_categories.append((cat, name))125 _selected_op_categories.append((cat, name))
@@ -120,13 +137,7 @@ def _collect_op(cat, name, torch_extension):
120 )137 )
121 )138 )
122 139 
123- csrc_dir = os.path.join(torch_extension, "csrc")140+ _collect_cpp(os.path.join(torch_extension, "csrc"))
124- if os.path.isdir(csrc_dir):
125- for cpp in os.listdir(csrc_dir):
126- if cpp.endswith(".cpp"):
127- _op_cpp_files.append(
128- (os.path.join("csrc", cat, cpp), os.path.join(csrc_dir, cpp))
129- )
130 141 
131 142 
132for cat in sorted(os.listdir(OPS_NN_ROOT)):143for cat in sorted(os.listdir(OPS_NN_ROOT)):
@@ -198,16 +209,7 @@ if os.path.isdir(_staged_ops_dir):
198 graph_src,209 graph_src,
199 )210 )
200 )211 )
201- csrc_dir = os.path.join(op_path, "csrc")212+ _collect_cpp(os.path.join(op_path, "csrc"))
202- if os.path.isdir(csrc_dir):
203- for cpp in os.listdir(csrc_dir):
204- if cpp.endswith(".cpp"):
205- _op_cpp_files.append(
206- (
207- os.path.join("csrc", cat, cpp),
208- os.path.join(csrc_dir, cpp),
209- )
210- )
211 213 
212 214 
213_sorted_selected_op_categories = sorted(set(_selected_op_categories))215_sorted_selected_op_categories = sorted(set(_selected_op_categories))
@@ -252,6 +254,15 @@ class BuildPyWithOps(_build_py):
252 base_dir = os.path.join(build_pkg, subdir)254 base_dir = os.path.join(build_pkg, subdir)
253 if not os.path.isdir(base_dir):255 if not os.path.isdir(base_dir):
254 continue256 continue
257+ if subdir == "csrc":
258+ for name in os.listdir(base_dir):
259+ op_name = name[:-4] if name.endswith(".cpp") else name
260+ if op_name not in _selected_ops and name != "__pycache__":
261+ target = os.path.join(base_dir, name)
262+ if os.path.isfile(target):
263+ os.remove(target)
264+ logger.info("removing centralized %s/%s", subdir, name)
265+ continue
255 for cat in os.listdir(base_dir):266 for cat in os.listdir(base_dir):
256 cat_dir = os.path.join(base_dir, cat)267 cat_dir = os.path.join(base_dir, cat)
257 if not os.path.isdir(cat_dir):268 if not os.path.isdir(cat_dir):
@@ -382,7 +393,11 @@ setup(
382 author="CANN",393 author="CANN",
383 license="CANN Open Software License Agreement Version 2.0",394 license="CANN Open Software License Agreement Version 2.0",
384 url="https://gitcode.com/cann/ops-nn/tree/master/torch_extension",395 url="https://gitcode.com/cann/ops-nn/tree/master/torch_extension",
385- install_requires=["torch>=2.6.0", "torch_npu"],396+ install_requires=[
397+ "ninja",
398+ "torch",
399+ "torch_npu",
400+ ],
386 packages=_all_packages,401 packages=_all_packages,
387 package_data={PACKAGE_NAME: _non_python_files(_src_path)},402 package_data={PACKAGE_NAME: _non_python_files(_src_path)},
388 entry_points={"cann_ops_nn.ops": _entry_points} if _entry_points else {},403 entry_points={"cann_ops_nn.ops": _entry_points} if _entry_points else {},