ops-gnn Development Guide
This document provides guidance on development environment setup, operator development, and testing for the ops-gnn library.
1. Environment Setup
1.1 Install CANN
Pre-installation
For offline installation, click here to download the CANN package and upload it to any path on the installation environment.
Install CANN
CANN 9.1.0-beta.1 or later is required. Other versions are not currently supported.
chmod +x Ascend-cann-toolkit_${VERSION}_linux-$(arch).run
./Ascend-cann-toolkit_${VERSION}_linux-$(arch).run --install
Where ${VERSION} is the CANN version (e.g., 9.1.0) and $(arch) is the CPU architecture.
Post-installation Configuration
source /usr/local/Ascend/cann-9.1.0-beta.1/bin/setenv.bash
If CANN is installed in a different path, replace with the actual path.
1.2 Detailed CANN Installation Guide
Visit Ascend Documentation → CANN Community Edition → Software Installation for detailed installation steps based on your environment, OS, and use case.
1.3 Dependencies
ops-gnn depends on the following components:
| Component | Version | Description |
|---|---|---|
| Python | 3.9+ | |
| CMake | 3.18+ | C++ build system |
| GCC | 7.0+ | C++17 compiler |
| PyTorch | 2.7+ | Ascend-adapted version from Ascend Community |
| torch_npu | 26.0.0 | PyTorch NPU device backend |
| CANN Toolkit | 9.1.0-beta.1+ | AscendC compiler, Bisheng compiler, runtime libraries |
torch and torch_npu must be obtained from the Ascend Community Download Page for Ascend hardware-adapted versions.
# System toolchain
sudo apt install build-essential cmake
# Test dependencies (optional)
pip3 install pytest pytest-cov
1.4 Install ops-gnn
# Activate CANN environment
source /usr/local/Ascend/cann-9.1.0-beta.1/bin/setenv.bash
# Method 1: pip development mode
cd /path/to/ops-gnn
pip install -e .
# Method 2: Build script
cd scripts
./build.sh python
# Method 3: CMake manual build
mkdir build_cmake && cd build_cmake
cmake ..
cmake --build .
2. Project Architecture
2.1 Layered Architecture
ops-gnn adopts a layered architecture of PyTorch extension + AscendC kernel:
+---------------------------------------------+
| Python Interface Layer |
| python/ops_gnn/<op>.py |
| (Type annotations, parameter preprocessing,|
| documentation) |
+---------------------------------------------+
| PyTorch Binding Layer |
| csrc/pybind.cpp |
| (PYBIND11_MODULE registration, param map) |
+---------------------------------------------+
| Host-side Operator Layer |
| csrc/npu/host/<op>/<op>.cpp/.h |
| (Argument parsing, Tiling calc, |
| Kernel launch, stream management) |
+---------------------------------------------+
| AscendC Kernel Layer |
| csrc/npu/kernel/<op>/<op>_kernel.cpp/.h |
| (AscendC SIMT/VF programming, |
| vector computation, data movement) |
+---------------------------------------------+
Architecture Description:
- Python Interface Layer: Provides user-friendly Python API with type annotations, docstrings, and usage examples
- PyTorch Binding Layer: Exposes C++ functions to Python via pybind11, handles Tensor parameter passing
- Host-side Operator Layer: Runs on host CPU, responsible for parameter validation, Tiling data computation, AscendC Kernel launch, and Stream synchronization
- AscendC Kernel Layer: Runs on NPU device (AIV cores), implements core computation logic
2.2 Directory Structure
ops-gnn
├── csrc/ # C++/AscendC source code
│ ├── pybind.cpp # PyTorch binding code
│ └── npu/ # NPU-related code
│ ├── host/ # Host-side code (organized by operator)
│ │ ├── add_sample/
│ │ │ ├── add_sample.h # Host interface declaration
│ │ │ └── add_sample.cpp # Host implementation (Tiling + Launch)
│ │ └── segment_max_csr/
│ │ ├── segment_max_csr.h
│ │ └── segment_max_csr.cpp
│ └── kernel/ # AscendC kernel implementation (by operator)
│ ├── add_sample/
│ │ ├── add_sample_kernel.h # Kernel Launch interface
│ │ └── add_sample_kernel.cpp # Kernel implementation (AscendC SIMT)
│ └── segment_max_csr/
│ ├── segment_max_csr_kernel.h # Kernel Launch interface
│ ├── segment_max_csr_kernel.cpp # Kernel entry + template instantiation
│ ├── segment_max_csr_kernel_impl.h # Kernel core implementation class
│ └── segment_max_csr_tiling.h # Tiling data structure
├── docs/ # Documentation
├── python/ # Python source code
│ └── ops_gnn/ # Python package
│ ├── __init__.py # Package init, export list
│ ├── add_sample.py # add_sample Python interface
│ ├── segment_max_csr.py # segment_max_csr Python interface
│ └── typing.py # Type alias definitions
├── test/ # Test directory
│ ├── test_import.py # Import verification test
│ ├── test_example.py # add_sample operator test
│ └── test_segment_max_csr.py # segment_max_csr operator test
├── scripts/ # Build scripts
│ └── build.sh # Unified build script (python/cpp/all)
├── cmake/ # CMake configuration
│ └── OpsGNNConfig.cmake.in # CMake package config template
├── CMakeLists.txt # CMake build configuration
├── setup.py # Python install script (setuptools + cmake)
├── setup.cfg # setuptools configuration
├── pyproject.toml # Modern Python project config
├── MANIFEST.in # Packaging manifest
└── LICENSE # CANN License
2.3 Core File Descriptions
| File | Description |
|---|---|
csrc/pybind.cpp |
PyTorch binding entry, registers all C++ operators to Python via PYBIND11_MODULE |
csrc/npu/host/<op>/<op>.h |
Host-side operator interface declaration |
csrc/npu/host/<op>/<op>.cpp |
Host-side operator implementation: Tensor dimension parsing, Tiling calc, dtype dispatch, Stream management |
csrc/npu/kernel/<op>/<op>_kernel.h |
Kernel Launch function declaration (Host-side call entry) |
csrc/npu/kernel/<op>/<op>_kernel.cpp |
Kernel Launch implementation + explicit template instantiation + <<<>>> launch syntax |
csrc/npu/kernel/<op>/<op>_kernel_impl.h |
AscendC Kernel core class implementation (Init → Process → Compute pipeline) |
csrc/npu/kernel/<op>/<op>_tiling.h |
Tiling data structure definition (parameter struct passed to Device side) |
python/ops_gnn/<op>.py |
Python interface: type annotations, default value handling, calls _pybind.<op> |
python/ops_gnn/__init__.py |
Package entry, imports from modules and registers to __all__ |
python/ops_gnn/typing.py |
Type aliases (Tensor, OptTensor) |
3. Coding Standards
3.1 Naming Conventions
C++ Layer:
- File names: lowercase underscore-separated, e.g.,
segment_max_csr_kernel.h - Function names: PascalCase, e.g.,
LaunchSegmentMaxCsrKernel - Struct names: PascalCase, e.g.,
SegmentMaxCsrTilingData - Class names: PascalCase, e.g.,
SegmentMaxCsrKernel - Template parameters: PascalCase or single uppercase letter, e.g.,
typename T
Python Layer:
- File names: lowercase underscore-separated, e.g.,
segment_max_csr.py - Function names: lowercase underscore-separated, e.g.,
segment_max_csr - Consistent with PyTorch style
3.2 Code Style
- C++ follows C++17 standard, uses
#pragma oncefor header guards - AscendC Kernel uses
__aicore__,__global__,__gm__modifiers - Use APIs under namespace
AscendC - Python interfaces use type annotations (
Tensor,OptTensor,Optional[Tensor]) - Each file includes CANN Open Software License copyright header
3.3 File Organization
Each operator strictly follows the following file split:
csrc/npu/
├── host/<op>/
│ ├── <op>.h # Host interface declaration
│ └── <op>.cpp # Host implementation
└── kernel/<op>/
├── <op>_kernel.h # Kernel Launch declaration
├── <op>_kernel.cpp # Kernel Launch implementation + template instantiation
├── <op>_kernel_impl.h # Kernel core class (simple ops can merge into kernel.cpp)
└── <op>_tiling.h # Tiling struct
4. Build System
4.1 CMake Build
CMakeLists.txt compiles AscendC Kernels and PyTorch binding library:
- Uses Bisheng compiler to compile Kernel source files into
libopsgnn_npu_kernel.so - Compiles
csrc/pybind.cpp+ host-side code into_pybind.so - Outputs artifacts to
output/kernel/
Key CMake variables:
| Variable | Description | Default |
|---|---|---|
NPU_ARCH |
NPU architecture | dav-3510 (950) / dav-2201 (910B) |
WITH_PYTHON |
Enable Python binding build | ON |
ASCEND_HOME_PATH |
CANN installation path | Read from env |
Python3_ROOT_DIR |
Python root directory | Auto-exported by build.sh |
4.2 setuptools Build
setup.py invokes CMake to compile and generate a wheel package. build_with_cmake() cleans old CMakeCache.txt before building to avoid path inconsistencies from pip temp directories.
5. Testing Guide
5.1 Running Tests
pytest test/ -v # Run all tests
pytest test/test_segment_max_csr.py -v # Single operator test
pytest test/test_segment_max_csr.py::test_func -v # Single test case
5.2 Test Writing Pattern
torch.npu.set_device(4)to specify NPU devicetorch.manual_seed(42)for reproducibility- Use
.npu()method to create NPU Tensor (e.g.,torch.randint(...).npu()) - Call
ops_gnn.<op>(...) - Verify results: device type, shape, numerical correctness
5.3 Test Coverage Requirements
| Scenario | Description |
|---|---|
| Basic functionality | Typical input |
| Different dtypes | float32, float16, int32, int16 |
| Edge cases | Empty segment, single element |
| Optional parameters | optional_out |
| Multi-dimensional input | 2D/3D Tensor |
| Broadcast scenarios | indptr broadcasting |
6. Operator Development Walkthrough
This section uses segment_max_csr as an example to illustrate the operator development process.
6.1 Call Chain Analysis
User Code
└─→ ops_gnn.segment_max_csr(src, indptr, optional_out)
│ python/ops_gnn/segment_max_csr.py
│ (default value handling: None → empty Tensor)
└─→ _pybind.segment_max_csr(src, indptr, optional_out)
│ csrc/pybind.cpp
│ (PYBIND11_MODULE registration)
└─→ segment_max_csr() [Host]
│ csrc/npu/host/segment_max_csr/segment_max_csr.cpp
│ (dimension parsing, Tiling fill, dtype dispatch, Stream management)
└─→ LaunchSegmentMaxCsrKernel<T>()
│ csrc/npu/kernel/segment_max_csr/segment_max_csr_kernel.cpp
│ (get AIV core count, <<<>>> launch)
└─→ segment_max_csr_kernel<T> [Device]
│ segment_max_csr_kernel_impl.h
│ (Init → Process → Compute pipeline)
Each layer's responsibilities:
- Python (
python/ops_gnn/<op>.py): Type annotations, optional param defaults, calls_pybind.<op> - PyBind (
csrc/pybind.cpp):m.def("<op>", &func, py::arg(...), ...)registers C++ function - Host (
csrc/npu/host/<op>/): Tensor dimension parsing → Tiling calc →torch::emptycreate output →aclrtCreateStream→ dtype dispatch launch →aclrtSynchronizeStream→ destroy Stream - Kernel Launch (
csrc/npu/kernel/<op>/<op>_kernel.cpp):GetCoreNumAiv()get core count →<<<coreNum, nullptr, stream>>>launch → explicit template instantiation - Kernel Impl (
<op>_kernel_impl.h):Init()parse Tiling + allocate Buffer/Event →Process()assign work range per Block →Compute()double-buffer pipeline (DataCopy → Max/Add → DataCopy)
6.2 New Operator File Checklist
Using segment_max_csr as a template, each new operator needs:
csrc/npu/host/<op>/
├── <op>.h # torch::Tensor <op>(torch::Tensor ...);
└── <op>.cpp # Host implementation
csrc/npu/kernel/<op>/
├── <op>_kernel.h # template<typename T> void Launch<Op>Kernel(...);
├── <op>_kernel.cpp # Launch impl + template instantiation
├── <op>_kernel_impl.h # Kernel core class (Init/Process/Compute)
└── <op>_tiling.h # struct <Op>TilingData { uint32_t ... };
python/ops_gnn/
└── <op>.py # Python interface
test/
└── test_<op>.py # Unit test
Additionally, two files must be modified:
csrc/pybind.cpp:#include "host/<op>/<op>.h"+m.def("<op>", ...)python/ops_gnn/__init__.py:from .<op> import <op>+ add to__all__
Simple operators can merge files: omit _tiling.h when no Tiling, merge _kernel_impl.h into _kernel.cpp when logic is simple (see add_sample).
6.3 Two Kernel Modes
| Feature | add_sample | segment_max_csr |
|---|---|---|
| File count | 1 kernel.cpp | kernel + impl + tiling (4 files) |
| Programming model | SIMT (__simt_vf__ + VF_CALL) |
Kernel class (TPipe + Buffer + Event) |
| Data movement | Direct GM read/write | DataCopy + double-buffer pipeline |
| Tiling | None (scalar params direct) | Tiling struct |
| Applicable | Element-wise ops | Reductions, segmentation, multi-stage pipelines |
6.4 Development Process Summary
- Create directory structure and files following
segment_max_csr - Define Tiling struct (only
uint32_ttypes, no pointers) - Implement Kernel core class (Init → Process → Compute), use TPipe + Event for double-buffer pipeline
- Implement Launch function, wrap
<<<>>>with#pragma GCC diagnostic - Host side: dimension parsing → Tiling fill →
torch::empty→aclrtCreateStream→ dtype dispatch launch → sync → destroy Stream - Register with
m.def(...)inpybind.cpp - Write Python interface (type annotations + docstring + default handling)
- Update
__init__.pyexports - Write tests (at minimum: basic functionality, multiple dtypes, edge cases)