This skill focuses on Ascend C operators(such as op_host / op_kernel, tiling, CANN build chain) once the implementation is ready, how tointegrate it into torch_npu and make it usable in PyTorch: file placement within the project, Host registration, compilation and packaging, installation and verification.Prioritize minimal intrusion and reproducible builds; if the user already has an operator repository, by default complete the integration within their project; only reference or use the op-plugin repository when needed. Subsequent sections of the main text will elaborate on environment and step details by scenario.
Overview of Integration Approaches
From the PyTorch side, custom NPU operators commonly have 4 integration routes:
torch.library/TORCH_LIBRARY[_IMPL]:- This is the main route for formally integrating into the PyTorch operator system, suitable for scenarios that require schema,
torch.ops.*invocation, and long-term maintenance - Formal operator integration defaults to primarily using this route
- This is the main route for formally integrating into the PyTorch operator system, suitable for scenarios that require schema,
Pybind:- Suitable for quickly exposing C++ functions to Python for joint debugging
- But it is not a complete PyTorch operator registration route and is not suitable as the default solution for formal operator integration
- It can serve as an auxiliary route when quick verification is needed; for detailed instructions see references/pybind.md
op-plugin/OpCommand:- Suitable for scenarios where the underlying graph operator already exists and only a PyTorch host wrapper layer needs to be added
- Suitable for scenarios where the underlying operator already exists and only a PyTorch Host wrapper needs to be added
- Graph mode / graph integration route (such as TorchAir):
- Suitable for scenarios that explicitly need to support graph mode compilation and graph optimization
- The current skill only provides basic reminders and does not treat it as the default mainline
Selection Recommendations
- The goal is formal operator integration: prefer the
torch.libraryroute - The goal is only quick verification of the kernel or interface joint debugging: you may temporarily consider
Pybind - There is already an underlying CANN /
xpu_kernelgraph operator: preferOpCommand - The goal is graph mode, graph integration, compilation optimization: TorchAir/graph mode integration requires additional evaluation and the ordinary eager integration process should not be directly applied
- If you only want to first verify "whether a C++ function can be called from Python" and do not yet require
torch.ops.*, schema, or long-term maintenance, you can see references/pybind.md
Minimal Closed Loop
No matter which route is ultimately chosen, it is recommended to first ensure the minimal closed loop is established:
- Environment is usable:
torch_npu, CANN, and the NPU runtime environment are normal - Core implementation is clear: whether it is a self-developed kernel or reusing an existing graph operator
- Interface exposure is complete: it can be called stably from the Python side
- Build artifacts are reproducible: it can be recompiled and reinstalled
- Tests can run: there is at least one smoke test verifying that registration and invocation succeed
If the user asks "how to use this skill"
If the user explicitly wants to know "how can I ask questions, how can I trigger this skill", you can provide a few representative prompt examples, preferably selected by scenario from references/prompt-examples.md ; there is no need to show all of them at once.
0. Important Principles (Must Be Followed)
- Prioritize "integration only"; do not modify operator implementations unless necessary:
- By default, do not modify directories and files related to the operator's own implementation (e.g.
op_host/、op_kernel/、tiling、json、infershape)。 - Prefer completing exposure and invocation by adding an independent integration layer (
framework/or an independent{op_name}_extension/) (especially when reusing a graph operator /OpCommand). - Only consider changing the operator implementation in the following cases: the user explicitly requests to "implement/fix the operator itself", or the existing operator is unavailable and the integration layer cannot work around it.
- By default, do not modify directories and files related to the operator's own implementation (e.g.
- Integration must be accompanied by an executable verification script:
- Whether building inside the container or driving an in-container build from outside the container, each integration must provide a script under
test/. - Minimum requirements:smoke test(can import/load_library, can call
torch.ops.npu.<op>, check output dtype/shape). - Recommended: clearly document the test run command in the README; when verifying, try to execute outside the project directory (e.g.
/tmp), to avoid importing local source code and causing misjudgment.
- Whether building inside the container or driving an in-container build from outside the container, each integration must provide a script under
1. Quick pre-check and solution selection
- First decide whether to use Plan A or Plan B:
- If you already have an operator implementation project locally (e.g. one containing
op_host、op_kernel、framework, tests, or build files in axpu_kernel/ custom-ops / AscendC repository), prefer using Plan A。 - Only use Plan B:
- the user explicitly requests integration based on
op-plugin; - the operator team requires the final artifact to land in the
op-plugindirectory tree; - Plan A cannot satisfy the required packaging, build, or test process.
- the user explicitly requests integration based on
- If you already have an operator implementation project locally (e.g. one containing
- Check whether
torch_npuis already available in the current environment:- Run:
python - << 'EOF'import torch; import torch_npuprint("torch:", torch.__version__)print("torch_npu:", getattr(torch_npu, "__version__", "unknown"))print("npu available:", torch.npu.is_available())EOF
- If the import succeeds and
npu available: True, you can usually continue using Plan A, and go directly toSection 4to start operator integration.
- Run:
- If
torch_npuis missing or unavailable: then consider proceeding to Section 2 of Plan Bto prepare theop-pluginenvironment. - The following operations must usethe same Python environment(the same venv/conda):
- Build
torch_npu - Build custom operators
- Install wheel
- Run tests
- Build
Interaction conventions between the in-container environment and the out-of-container command line
If NPU / CANN / torch_npu the environment is inside the container, while the agent can only be driven from outside the container via the command line, then preferably interact as follows:
-
First confirm 3 pieces of information:
- Container name or container ID (e.g. the target container seen in
docker ps/podman ps) - The mapping between the host workspace path and the in-container mount path
- The path to the Python environment used for building/testing inside the container
- Container name or container ID (e.g. the target container seen in
-
All pre-checks, builds, installations, and tests are executed inside the container:
- Do not use the host Python to determine whether
torch_npuis available - Uniformly change to
docker exec <container> bash -lc '<cmd>'orpodman exec <container> bash -lc '<cmd>'
- Do not use the host Python to determine whether
-
All generated files must reside in a directory that is a "shared mount between the host and the container":
- In this way, files created/edited by the agent on the host side can be seen directly inside the container
- In this way, the artifacts built inside the container
build/、dist/, and test logs, can also be read directly on the host side - Do not write key integration files to temporary paths inside the container, otherwise they cannot be continuously edited and reused outside the container
-
Every time
execthe environment must be re-established:- Non-interactive
bash -lcwill not inherit the temporary environment of the previous shell - Therefore, each execution should explicitly include
source <CANN>/set_env.sh && <venv 激活命令> && <实际命令>
- Non-interactive
-
Prefer to merge "multi-step dependent actions" into the same container command:
- For example
source ... && python setup.py bdist_wheel && pip install ... && cd test && python test.py - This can reduce errors caused by state switching inside and outside the container
- For example
-
Import/registration verification should likewise be performed inside the container, and as far as possible outside the project directory:
- Avoid the source directory inside the container taking precedence in
sys.path, which would cause importing the local source by mistake instead of the installed wheel
- Avoid the source directory inside the container taking precedence in
-
If any of the container name, mount path mapping, or Python environment is unclear, ask the user first; do not guess. For more on "driving build/verification inside the container from the command line outside the container (including OpCommand and other templates and fixes for typical errors)", see the reference documentation:
2. Install the op-plugin environment only under Plan B
This stepis not the default path. Execute it only when it is certain that Plan B is adopted. When executing, also try to keep itidempotent: prefer reusing the existing environment, and rebuild only when it is missing or the version does not match.
-
2.1 Source the CANN environment once per shell
source <CANN install path>/set_env.sh(for example/usr/local/Ascend/ascend-toolkit/set_env.sh)
-
2.2 Check whether the existing
op-pluginrepository andtorch_npualready match- If you already have a
op-plugincheckout, and thetorch_npuin the same Python environmentskip the rebuild:- Example check command:
python - << 'EOF'import torch, torch_npuprint("torch:", torch.__version__)print("torch_npu:", getattr(torch_npu, "__version__", "unknown"))print("npu available:", torch.npu.is_available())EOF
- If the import succeeds,
npu available: True, and the version meets the project requirements, you can proceed directly toSection 4for operator integration.
- Example check command:
- If you already have a
-
2.3 If
torch_npuis missing locally, or a specific version is required, prepare/switch the op-plugin environment- Clone
op-plugin(the branch must match the targettorch_npuversion):git clone --branch 7.3.0 https://gitcode.com/ascend/op-plugin.git && cd op-plugin
- Build:
bash ci/build.sh --python=3.9 --pytorch=v2.7.1-7.3.0- Adjust according to the target Python / PyTorch version
--pythonand--pytorch; for the version correspondence, see references/reference.md
- Install:
cd dist && pip install dist/torch_npu-*.whl- After installation, re-run the pre-checks in Section 1 to confirm that
torch_npuis available andnpu available: True
- Clone
Dependencies include:torch_npu, CANN. When building, prefer using the torch_npu official Docker environment.op-plugin For the version matrix of branches with PyTorch/Python/GCC, see references/reference.md。
3. File placement rules: Plan A and Plan B
- Plan A (preferred, lightweight)
- Withinthe operator implementation projectcomplete the integration.
- If the project already has a
framework/directory, prioritize placing the integration files into it. - Ifthere is no
framework/, then create a separate directory, for example{project_root}/{op_name}_extension/or{project_root}/framework/{op_name}_extension/。 - Unless the integration itself genuinely requires modifying the operator implementation, try not to change the existing
op_host/、op_kernel/, tiling, json, and other implementation files. - Include only the minimal necessary integration artifacts: host wrapper, Python loading/wrapping, build scripts, tests, README.
- Plan B (op-plugin reference/fallback)
- Use the
op-pluginrepository as the integration workspace. - Newly generated operator registration or host-side
.cppfiles should be placed intoop_plugin/ops/in an appropriate subdirectory, and the directory choice should follow the operator domain and existing naming conventions. - Other integration process files, such as demo wrappers,
setup.py, tests, sample scripts, README, etc., should be placed intoexample/(or the repository's existing example directory convention). - Treat
op-pluginas areference implementation path, not as the default location.
- Use the
4. Integration Mode Selection
General process for adding a new operator: first determinewhether the target operator already exists(for example, built into CANN/ops-nn, or already implemented in xpu_kernel / custom ops) → if it can be reused, prefer Pattern C (OpCommand) → otherwise, depending on whether workspace/tiling is needed, implement your own AscendC kernel in Pattern A or Pattern B.
- If the operator already has a complete implementation(for example, the CANN built-in operator
layer_norm_v3, or an operator in thexpu_kernel/ custom ops repository that you have already built and installed):- There is no need to compile a new AscendC kernel;
- you only need to add a lightweight host wrapper in the custom extension, call the graph operator name through
at_npu::native::OpCommand, and expose it astorch.ops.npu.*; - CMake only needs to link
torch_npu; workspace/tiling is handled internally by the underlying graph operator. - For the
xpu_kerneloperator: the graph operator name comes fromop_definOP_ADD(OpClassName). The prerequisite is thatxpu_kernelhas already been built and installed; otherwise CANN cannot load the graph operator at runtime.
- If the operator does not yet exist and you only have your own AscendC kernel:
- Then continue with Pattern A or Pattern B, implementing the kernel, tiling, and host wrapper yourself.
Patterns A, B, and C can coexist in the same project. The core principle is:when existing system capabilities can be reused, prioritize reuse and do not reinvent the wheel.
Pattern A: No workspace (reference add)
- Kernel: has only inputs and outputs (and optional scalars), and does not require workspace/tiling. Files are generally placed in
csrc/kernel/{kernel_name}_custom.cpp, and the Ascend C process isCopyIn -> Compute -> CopyOut。 - Host: is only responsible for allocating outputs (for example
at::empty_like(x)orat::empty(...)), and then callsEXEC_KERNEL_CMD({kernel_name}, blockDim, input..., output[, scalars]). It needs to includeaclrtlaunch_{kernel_name}.h。 - CMake: Use
ascendc_library(no_workspace_kernel STATIC csrc/kernel/{kernel_name}_custom.cpp)(or define a separate target for each kernel), and link that library into the final shared extension.
Pattern B: Has workspace and/or tiling (reference matmul_leakyrelu)
- Kernel: Requires workspace (and optionally tiling). Files are generally
csrc/kernel/{kernel_name}_custom.cpp, likewise followingCopyIn -> Compute -> CopyOut. When building, addHAVE_WORKSPACE, and if tiling is used also addHAVE_TILING。 - Host: is responsible for allocating the output and workspace tensors (the size comes from the platform interface or user input), and if needed also allocating the tiling tensor and calling the tiling generation logic. Finally call
EXEC_KERNEL_CMD({kernel_name}, blockDim, input..., output, workspace[, tiling]). Need to includeaclrtlaunch_{kernel_name}.h。 - CMake: uses
ascendc_library(workspace_kernel STATIC csrc/kernel/{kernel_name}_custom.cpp), and viaascendc_compile_definitions(workspace_kernel PRIVATE -DHAVE_WORKSPACE -DHAVE_TILING)add macro definitions (if tiling is not used, removeHAVE_TILING). If tiling logic exists, also addcsrc/host/tiling/*.cppto the host source file list, and linkworkspace_kernelinto the final shared extension.
Pattern C: Reuse an existing operator (OpCommand pattern)
When the target operator is already fully implemented (for example, built into CANN, or xpu_kernel / custom ops are installed and available), use Pattern C:
-
Basic idea: directly via
OpCommandcall the graph operator name, without adding a new AscendC kernel. -
OpCommandinput/output naming: If the graph operator is inop_defdefines explicit input/output names, they must be matched exactly via the second parameterdescName, for example.Input(tensor, "inputGradY")、.Output(tensor, "outputGradX"), so that it can map correctly to the graph operator. -
Host layer example (
LayerNormV3, built into CANN)- PyTorch API design:
torch.ops.npu.layer_norm_v3(x, gamma, beta, begin_norm_axis, begin_params_axis, eps) -> (y, mean, rstd)
- Implementation points:
- Ensure
xis on the NPU:x.device().type() == PrivateUse1 - Construct
y/mean/rstdoutput:y:at::empty_like(x)mean/rstd: shape is[A1...Ai, 1...1], wherei = begin_norm_axis
- use
OpCommand:.Name("LayerNormV3").Input(x).Input(gamma).Input(beta).Output(y).Output(mean).Output(rstd).Attr("begin_norm_axis", (int64_t)begin_norm_axis).Attr("begin_params_axis", (int64_t)begin_params_axis).Attr("epsilon", (float)eps).Run()
- Ensure
- PyTorch API design:
-
Host layer example (
LayerNormV4)- PyTorch API design:
torch.ops.npu.layer_norm_v4(x, int[] normalized_shape, Tensor? gamma=None, Tensor? beta=None, float eps=1e-5) -> (Tensor, Tensor, Tensor)
- Implementation points:
- C++ signature uses
at::IntArrayRef normalized_shape、c10::optional<at::Tensor> gamma_opt/beta_opt - output:
y = at::empty_like(x)mean/rstdhas shape[A1...Ai, 1...1], whereAiis the axis not participating in normalization (that is, the dimensions beforeinput.dim() - normalized_shape.size())
OpCommandcall method:.Name("LayerNormV4").Input(x).Input(normalized_shape)// host-side int list,OpCommandwill automatically handle H2D- Optional input handling:
- If
gamma_opthas a value:.Input(*gamma_opt), otherwise.Input()(an empty input will map toOPTIONAL_INPUT) - If
beta_opthas a value:.Input(*beta_opt), otherwise.Input()
- If
.Output(y).Output(mean).Output(rstd).Attr("epsilon", (float)eps).Run()
- C++ signature uses
- PyTorch API design:
-
CMake simplification method:
- No need for
ascendc_library, and no need for the tiling source file; keep only the host source:file(GLOB _SRCS csrc/host/*.cpp)add_library({pkg} SHARED ${_SRCS})
- Link only the necessary libraries:
target_link_libraries({pkg} PRIVATE torch_npu)
- Add include directories:
${TORCH_NPU_PATH}/include${TORCH_PATH}/includeandtorch/csrc/api/include${ASCEND_CANN_PACKAGE_PATH}/include(for dependencies such asgraph/types.h)- Add
${ASCEND_CANN_PACKAGE_PATH}/runtime/includeif necessary; in some environments, headers related toOpCommandwill continue to include runtime-sidegraph/*dependencies
- Practical advice: For host-only Pattern C, if the PyTorch in the current environment does not export
TorchConfig.cmake, do not strongly depend onfind_package(Torch); at this point, the usually safer approach is to directly usesetup.pyintorch.utils.cpp_extension.load(...)or an equivalent method to compile the shared library, and then load it throughtorch.ops.load_library(...).
- No need for
-
For
xpu_kernel/ custom ops, Pattern C description:- The graph operator name comes from
op_definOP_ADD(OpClassName)(for example,MoeInitRoutingGroupedMatmulGrad) - The input/output names come from definitions such as
this->Input("inputGradY")、this->Output("outputGradX"); when calling, write it as.Input(t, "inputGradY")、.Output(t, "outputGradX") - The output shape needs to be derived on the host side according to the infer shape logic (for example,
batch = expanded_row_idx.numel() / topk), and ensure it is consistent with the derivation result of the graph operator. - The precondition is that
xpu_kernel(or custom ops) has already been built and installed, otherwiseOpCommandwill report at runtime:"op not found" - Plan A placement: Prefer putting the host-only wrapper into the operator implementation project's
framework/or a newly created{op_name}_extension/。 - Plan B placement: If using op-plugin, put the host/registration
.cppinto an appropriate subdirectory ofop_plugin/ops/, and put the remaining integration files intoexample/。 - Engineering recommendations (from an out-of-container driver case):
setup.pymust be completed withtorch_npu/includeand CANN include (otherwiseOpCommand.h/graph/types.hwill fail to compile).- At the linking stage,
libtorch_npu.somust be linked (otherwise at runtimeundefined symbol OpCommand::Name) - Verification must be performed outside the source directory (to avoid importing the local package causing
.sonot to be found).
- The graph operator name comes from
-
Common notes:
OpCommand::AttrFor the integer parameters ofint64_t, it is recommended to uniformly useOpAttrMaker::Set, otherwisebool/int64_tmay have overload ambiguity.normalized_shapeshould be passed from the Python side asList[int], do not passTensor; on the C++ side useIntArrayRef- For optional tensor parameters, prefer using
Tensor?with an empty.Input(), do not use a placeholder tensor instead. - If a C API such as
aclnn_layer_norm*already exists at the lower layer, prefer calling the graph operator throughOpCommand, rather than manually handlingaclTensorDesc/aclDataBuffer/aclnn*GetWorkspaceSize setup.py: Useos.F_OKandos.X_OK, do not write a nonexistentos.path.F_OK- Do not mistakenly import the
TORCH_LIBRARY_*registration library as a Python extension module: If your.sois only responsible for registeringtorch.ops.npu.*, it usually does not provide aPyInit_*entry point; in this case, treat it as an ordinary shared library and load it throughtorch.ops.load_library(...), rather thanimport _C - If using
torch.utils.cpp_extension.load(...)to generate the Pattern C shared library, you should usually explicitly useis_python_module=False
Core conclusion:First determine whether the operator already exists (CANN or xpu_kernel); if it already exists, usually you only need to add a PyTorch host wrapper.
5. Kernel implementation
- Add
csrc/kernel/{kernel_name}_custom.cpp. The Ascend C logic followsCopyIn -> Compute -> CopyOut. Pattern A has no workspace; Pattern B uses workspace/tiling according to Ascend C docs . - The kernel entry name must be consistent with the name used in host and CMake: the generated header file is
aclrtlaunch_{kernel_name}.h, and the host side callsEXEC_KERNEL_CMD({kernel_name}, ...)。 - Add the corresponding
ascendc_libraryin CMake. For Pattern B, additionally addascendc_compile_definitionsvia-DHAVE_WORKSPACE, and optionally-DHAVE_TILING. Make sure this library is linked into the final shared library (e.g.,op_extension/lib{pkg}.so)。
6. PyTorch Integration: Host Layer
- Add
csrc/host/{op_name}.cpp, which contains:- Aten IR definition:
TORCH_LIBRARY_FRAGMENT(npu, m) { m.def("{op_name}(...) -> ..."); } - Implementation function: For example
run_xxx, responsible for allocating the output; if it is Pattern B, you also need to prepare the workspace and tiling, and finally callEXEC_KERNEL_CMD({kernel_name}, blockDim, ...) - Registration code:
TORCH_LIBRARY_IMPL(npu, PrivateUse1, m) { m.impl("{op_name}", TORCH_FN(run_xxx)); }
- Aten IR definition:
- It is recommended to reuse the
utils.hfrom the cpp_extension example (e.g.,EXEC_KERNEL_CMD、ConvertTypesand other helper logic). The scalar passed toEXEC_KERNEL_CMDmust be anlvalue, do not pass an rvalue directly. - For Pattern B, the host implementation must also be responsible for computing the workspace size (e.g., obtained through the platform interface), calling the tiling generation logic, and passing the workspace tensor together with the tiling tensor to
EXEC_KERNEL_CMD。
7. Build and Run
SOC_VERSION: InCMakeLists.txt, setset(SOC_VERSION "Ascendxxxyy" ...). The chip name can be obtained vianpu-smi info, generally written asAscend+ chip name (e.g.,Ascend910B)。- Build the wheel:
python setup.py bdist_wheel - Install (force overwrite the old version):
cd dist && pip install --force-reinstall *.whl- This avoids mistakenly using the old version of
{pkg}in site-packages, which would cause the new operator to be "hidden"
- Run the test:
cd test && python test.py
Notes:
- In Plan A , these commands are usually executed in the operator implementation project's
framework/or a standalone integration directory - In Plan B , these commands are usually executed in the
op-pluginexample workspace that wraps this operator - If the environment is in a container, the actual execution location of the above commands should beinside the container; the host machine is only responsible for preparing files, issuing the
docker exec/podman execcommands, and reading artifacts from the shared mounted directory
8. Writing Tests
- Unified steps:
import {pkg}(load.soand complete registration) → construct input tensors (you can first create them on the CPU and then.npu(), or directly create them on the NPU) → calltorch.ops.npu.{op_name}(...)→ compute the CPU reference valuecpu_ref(an existing PyTorch operator or an equivalent formula) → useTestCase.assertRtolEqual(output, cpu_ref)(or an equivalent assertion within the project) for comparison. - Pattern A style (e.g.,
add):cpu_ref = torch.add(x, y)(or the corresponding equivalent PyTorch implementation) - Pattern B style (e.g.,
matmul_leakyrelu):cpu_ref = some_combination(e.g. LeakyReLU(matmul(a,b) + bias)) - For each new operator, add a new test method in
test/test.py(e.g.,test_xxx), and just follow the above pattern. There is no need to write an additional separatedemo.py; the test uniformly serves as the entry point for running and verification. - If it depends on
torch_npu.testing.testcase.TestCase, please installexpecttest:pip install expecttest - When doing import or registration checks, try not to run tests directly in thesource package root directory; otherwise, the local source directory may take precedence over site-packages and be imported by Python first, causing a false determination of "import failure" or a circular import. A more reliable approach is:
- First install the wheel
- Then execute from outside the project directory
python -c 'import {pkg}'or run the test
9. Required files and directory layout
Placeholder description:{pkg} represents the Python package name,{kernel_name} represents the kernel entry name,{op_name} represents the PyTorch API name.Naming must be consistent throughout. For example, if the kernel is called add_custom, then {op_name} should usually also be written as add_custom。
Plan A / Pattern A/B(with kernel, recommended layout):
<project_root>/
├── framework/ # if the project already has one, use it first
│ └── {pkg}/
│ ├── {pkg}/
│ │ ├── __init__.py
│ │ └── _load.py
│ ├── csrc/
│ │ ├── kernel/
│ │ │ └── {kernel_name}_custom.cpp
│ │ └── host/
│ │ ├── {op_name}.cpp
│ │ ├── utils.h
│ │ └── tiling/ # 可选,Pattern B 使用
│ │ └── *_tiling.cpp
│ ├── CMakeLists.txt
│ ├── setup.py
│ └── test/
│ └── test.py
└── ... # 原有 op_host/op_kernel/json 保持原位
If there is no framework/, create a new {project_root}/{op_name}_extension/in the same directory, keeping the internal structure consistent.
Plan A / Pattern C(host only, no kernel):
<project_root>/
├── framework/ or {op_name}_extension/
│ ├── {pkg}/
│ │ ├── __init__.py
│ │ └── _load.py
│ ├── csrc/host/
│ │ └── {op_name}.cpp # OpCommand only
│ ├── CMakeLists.txt
│ ├── setup.py
│ └── test/
│ └── test.py
└── ... # 算子实现继续保留在原有源码树中
Plan B(based on the op-plugin placement layout):
<op-plugin-root>/
├── op_plugin/
│ └── ops/
│ └── <chosen_domain_folder>/
│ └── {op_name}.cpp # host/registration .cpp,按领域归类放置
├── example/ or examples/
│ └── {op_name}_extension/
│ ├── {pkg}/
│ ├── csrc/
│ ├── CMakeLists.txt
│ ├── setup.py
│ ├── test/
│ └── README.md
└── ...
Naming consistency requirements: {kernel_name} must match both aclrtlaunch_{kernel_name}.h、EXEC_KERNEL_CMD({kernel_name}, ...) and the kernel source file name. The package name must also be consistent with the generated .so name and the name in setup.py .
For multiple operators: Under Pattern A/B, usually each kernel corresponds to one ascendc_library, each operator corresponds to one csrc/host/{op_name}.cpp, and a corresponding test.py is added in test_xxx。
10. End-to-end checklist
If you want to adopt acomplete, automated, close to demo-styleprocess, you can execute in the following order:
- First determine the placement plan:
- If you already have an operator implementation project, prefer using Plan A
- Only switch to
op-plugindelivery, or when Plan A is not suitable Plan B
- Prepare the environment and
torch_npu(usually done only once per machine):source <CANN install path>/set_env.sh- Execute the pre-checks in Section 1
- If
torch_npuis missing or unavailable, and Plan B is currently adopted, build and install according to Section 2
- Project build (execute after each modification of kernel / host / CMake):
- In
CMakeLists.txtset a supportedSOC_VERSION(for example, refer to thehost_config.cmakein the CANN support listascend910b2) python setup.py bdist_wheelcd dist && pip install --force-reinstall *.whl
- In
- Run tests (execute after each operator modification):
- In
test/test.py, as described in Section 8:import {pkg}(automatically loadlib{pkg}.so)- Call
torch.ops.npu.{op_name}(...), whereop_namemust match the actual operator name, do not keep it asmy_* - Compute the CPU reference value
cpu_ref - Use
assertRtolEqualto compare results
- Execute:
cd test && python test.py
- In
- Quickly confirm whether it is registered (optional debugging step):
python - << 'EOF'import torch, {pkg}# noqaprint([name for name in dir(torch.ops.npu) if "{op_name_hint}" in name])EOF- When the test reports "no attribute", you can use this method to quickly confirm whether the operator has actually been registered successfully.
11. References
The following documents provide supplementary information; the actual main process is still based on the current SKILL.md .
- references/README.md — Reference document index and reading instructions
- references/reference.md — Version matrix,
SOC_VERSION, common links - references/examples.md — General checklist for adding operators (Pattern A/B/C)
12. Supplementary resources
- references/reference.md — Version table,
SOC_VERSION、op-pluginand cpp_extension README links, Ascend C materials - references/examples.md — General checklist for adding operators (select mode → kernel/host → CMake → test)