skills/simple-vector-triton-gpu-to-npu/SKILL.md
将简单Vector类型Triton算子从GPU迁移到昇腾NPU。当用户需要迁移Triton代码到NPU、提到GPU到NPU迁移、Triton迁移、昇腾适配时使用。注意:无法自动迁移存在编译问题的算子。
npx skillsauth add Ascend/agent-skills simple-vector-triton-gpu-to-npuInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
本技能帮助用户将简单Vector类型的Triton算子从GPU迁移到昇腾NPU平台,提供完整的迁移流程、代码转换指南和精度验证方法。
当出现以下情况时使用本技能:
# 1. 分析源代码
# 使用 templates/analysis_template.md 生成语义分析报告
# 2. 最小化迁移
# 只修改设备:device='cuda' -> device='npu'
# 3. 运行测试
python test_your_kernel.py
# 4. 根据错误调整
# 参考 reference/troubleshooting.md 解决问题
✅ 支持迁移的算子类型:
❌ 暂不支持自动迁移的情况:
本技能采用NPU专用迁移策略,将GPU实现迁移为NPU实现。
⚠️ 在迁移前,必须先分析源代码的语义逻辑,生成分析报告
使用 templates/analysis_template.md 生成分析报告。
分析要点:
详细分析指南请参考 reference/analysis_guide.md。
# 安装依赖
pip uninstall triton # 卸载社区Triton
pip install triton-ascend
pip install torch-npu
# 验证安装
python -c "import torch_npu; print(torch_npu.npu.is_available())"
核心原则:先按照GPU源代码尝试,如果尝试有报错再进行后续调整
# 第一步:只修改设备指定
# device='cuda' -> device='npu'
x = torch.rand(size, device='npu')
# 第二步:运行测试
try:
result = kernel_npu(**test_inputs)
print("✅ 基础运行成功")
except Exception as e:
print(f"❌ 运行失败: {e}")
⚠️ 必须迁移的GPU专用API:
| GPU API | NPU API | 说明 |
|---------|---------|------|
| torch.cuda.is_available() | torch.npu.is_available() | 检查设备是否可用 |
| torch.cuda.empty_cache() | torch.npu.empty_cache() | 清空缓存 |
| torch.cuda.synchronize() | torch.npu.synchronize() | 同步设备 |
| torch.cuda.mem_get_info() | torch.npu.mem_get_info() | 获取内存信息 |
| device="cuda" | device="npu" | 设备指定 |
| @torch.compile | 删除 | NPU暂不支持torch.compile训练 |
# 直接使用NPU API
import torch_npu
with torch_npu.npu.device(device_index):
kernel[grid](...)
props = torch_npu.npu.get_device_properties(device)
sm_count = props.vector_core_num # Ascend910为48
NPU设备属性对照表: | GPU属性 | NPU属性 | 典型值(Ascend910) | |---------|---------|-------------------| | multi_processor_count | vector_core_num | 48 | | total_memory | total_memory | 62740MB | | name | name | 'Ascend910_9392' | | - | cube_core_num | 24 | | - | L2_cache_size | '192MB' |
根据遇到的错误类型,选择对应的解决方案:
| 错误类型 | 错误信息关键词 | 解决方案 | |---------|--------------|---------| | 编译错误 | compilation failed | 检查Triton语法兼容性 | | coreDim超限 | coreDim > UINT16_MAX | 增大BLOCK_SIZE或设置环境变量 | | UB溢出 | ub overflow | 使用子块切分策略 | | 精度问题 | NaN, Inf, 不匹配 | 检查逻辑运算符、mask使用 | | 性能问题 | 运行缓慢 | 优化内存访问、使用Tiling |
详细解决方案请参考 reference/troubleshooting.md。
迁移完成后必须进行精度验证:
def verify_accuracy(result, ref, dtype):
# 检查NaN/Inf
assert not torch.isnan(result).any(), "结果包含NaN"
assert not torch.isinf(result).any(), "结果包含Inf"
# 设置容差
if dtype in [torch.float16, torch.bfloat16]:
rtol, atol = 1e-3, 1e-3
elif dtype == torch.float32:
rtol, atol = 1e-4, 1e-4
else:
rtol, atol = 0, 0
torch.testing.assert_close(result, ref, rtol=rtol, atol=atol)
迁移前(GPU版本):
@triton.jit
def add_kernel(x_ptr, y_ptr, output_ptr, n_elements, BLOCK_SIZE: tl.constexpr):
pid = tl.program_id(axis=0)
offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
mask = offsets < n_elements
x = tl.load(x_ptr + offsets, mask=mask)
y = tl.load(y_ptr + offsets, mask=mask)
output = x + y
tl.store(output_ptr + offsets, output, mask=mask)
x = torch.rand(98432, device='cuda')
y = torch.rand(98432, device='cuda')
迁移后(NPU版本):
@triton.jit
def add_kernel(x_ptr, y_ptr, output_ptr, n_elements, BLOCK_SIZE: tl.constexpr):
pid = tl.program_id(axis=0)
offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
mask = offsets < n_elements
x = tl.load(x_ptr + offsets, mask=mask)
y = tl.load(y_ptr + offsets, mask=mask)
output = x + y
tl.store(output_ptr + offsets, output, mask=mask)
x = torch.rand(98432, device='npu')
y = torch.rand(98432, device='npu')
说明:示例中未添加care_padding=False,遵循"先确保功能正确"的原则。
更多示例请参考 reference/examples.md。
other应设为超出有效范围的值(如N)tl.load检查输入有效性,tl.store检查输出边界torch.cuda.* 必须替换为 torch.npu.* 或 torch_npu.npu.*vector_core_num 而非 multi_processor_count@torch.compile装饰器,必须删除| GPU API | NPU API | 说明 |
|---------|---------|------|
| torch.cuda.is_available() | torch.npu.is_available() | 检查设备是否可用 |
| torch.cuda.empty_cache() | torch.npu.empty_cache() | 清空缓存 |
| torch.cuda.synchronize() | torch.npu.synchronize() | 同步设备 |
| torch.cuda.mem_get_info() | torch.npu.mem_get_info() | 获取内存信息 |
| device="cuda" | device="npu" | 设备指定 |
| @torch.compile | 删除 | NPU暂不支持torch.compile训练 |
原则:先确保功能正确,再考虑性能优化
推荐流程:
care_padding=Falsecare_padding=False示例:
# Step 1: 初始迁移(不添加)
x = tl.load(x_ptr + offsets, mask=mask)
# Step 2: 功能验证通过后,可选的性能优化
# x = tl.load(x_ptr + offsets, mask=mask, care_padding=False)
⚠️ 警告:直接添加care_padding=False可能导致输出全为0或精度问题。
# ✅ 正确示例:load 和 store 使用各自的 mask
out_mask = rows_mask & cols_mask[None, :] # 输出边界检查
final_mask = out_mask & index_valid_mask[None, :] # 输入有效性检查
selected = tl.load(inp + inp_off, mask=final_mask, other=0.0)
tl.store(out + out_off, selected, mask=out_mask) # 正确!
| 操作 | mask 含义 | 应检查的内容 |
|------|----------|-------------|
| tl.load | 哪些输入位置需要读取 | 索引有效性、输入边界 |
| tl.store | 哪些输出位置需要写入 | 输出边界、行列范围 |
pip install triton-ascend torch-npu
⚠️ 注意:本节内容适用于以下场景:
对于简单Vector算子,通常只需要:
device='cuda'改为device='npu'grid = (num_core,)GPU使用逻辑网格(如3D网格),而NPU使用物理核心网格(1D网格)。为了优化NPU性能,需要将GPU风格的网格适配到NPU的物理核心架构。
原始GPU风格:
grid = (NV, NK, N * H) # 3D逻辑网格
kernel[grid](...)
优化后的NPU风格:
import torch_npu
import triton.runtime.driver as driver
def get_npu_properties():
device = torch.npu.current_device()
return driver.active.utils.get_device_properties(device)
num_core = get_npu_properties()["num_vectorcore"]
grid = (num_core,) # 1D物理核心网格
GPU内核入口:
i_v, i_k, i_nh = tl.program_id(0).to(tl.int64), tl.program_id(1).to(tl.int64), tl.program_id(2).to(tl.int64)
i_n, i_h = i_nh // H, i_nh % H
带任务分发的NPU内核入口:
core_id = tl.program_id(0)
task_num = NV * NK * N * H
knh_step = NK * N * H
nh_step = N * H
for task_id in tl.range(core_id, task_num, num_core):
i_v = task_id // knh_step
i_k = task_id % knh_step // nh_step
i_nh = task_id % knh_step % nh_step
i_n = task_id % knh_step % nh_step // H
i_h = task_id % knh_step % nh_step % H
# ... 原有内核逻辑
额外的NPU参数:
def kernel(...,
knh_step: tl.constexpr,
nh_step: tl.constexpr,
N: tl.constexpr,
task_num: tl.constexpr,
num_core: tl.constexpr,
...):
grid = (dim1, dim2, dim3, ...)tl.program_id(0), tl.program_id(1)等# 计算总任务数
task_num = dim1 * dim2 * dim3 * ... # 所有网格维度的乘积
# 计算每个维度的步长
# 3D网格(dim1, dim2, dim3)示例:
step_dim2_dim3 = dim2 * dim3
step_dim3 = dim3
# 在内核中:
# task_id = core_id + i * num_core
# dim1_idx = task_id // step_dim2_dim3
# dim2_idx = (task_id % step_dim2_dim3) // step_dim3
# dim3_idx = task_id % step_dim3
用任务分发循环替换直接的program_id索引:
# 之前:
i0 = tl.program_id(0)
i1 = tl.program_id(1)
i2 = tl.program_id(2)
# 之后:
core_id = tl.program_id(0)
for task_id in tl.range(core_id, task_num, num_core):
i0 = task_id // step_dim2_dim3
i1 = (task_id % step_dim2_dim3) // step_dim3
i2 = task_id % step_dim3
# ... 所有使用这些变量的代码都在循环内部
重要:从GPU风格迁移到NPU任务分发模式时,变量作用域发生变化。必须将所有使用循环内部变量的代码移到循环内部。
需要检查的常见变量:
pid_b, pid_h, pid_seq, i0, i1, i2等seq_len, T, B等(特别是当IS_VARLEN时可能被修改)seq_offset, bos, eos等需要移动的代码:
nchunks)错误示例:
# ❌ 错误:变量在循环内部定义,但在外部使用
core_id = tl.program_id(0)
for task_id in tl.range(core_id, task_num, num_core):
pid_b = task_id // h_step
pid_h = task_id % h_step
# ... 其他变量定义
# 错误:在循环外部使用循环内部定义的变量
nchunks = tl.cdiv(seq_len, CHUNK_SIZE) # seq_len未定义
ANGLE += pid_b * stride_angle_batch # pid_b未定义
正确示例:
# ✅ 正确:所有使用循环内部变量的代码都在循环内部
core_id = tl.program_id(0)
for task_id in tl.range(core_id, task_num, num_core):
pid_b = task_id // h_step
pid_h = task_id % h_step
seq_len = ... # 在循环内部定义
# 所有使用这些变量的代码都在循环内部
nchunks = tl.cdiv(seq_len, CHUNK_SIZE)
angle_ptr = ANGLE + pid_b * stride_angle_batch # 使用局部变量
# ... 后续所有计算
# 之前:
grid = (dim1, dim2, dim3)
kernel[grid](...)
# 之后:
num_core = get_npu_properties()["num_vectorcore"]
grid = (num_core,)
kernel[grid](
...,
knh_step=step_dim2_dim3,
nh_step=step_dim3,
N=dim1, # 或适当的映射
task_num=task_num,
num_core=num_core,
)
原始GPU版本(2D网格):
@triton.jit
def kernel_gpu(x_ptr, output_ptr, N, M, BLOCK_SIZE: tl.constexpr):
pid_n = tl.program_id(0)
pid_m = tl.program_id(1)
# 计算偏移
x = x_ptr + pid_n * M + pid_m * BLOCK_SIZE
# ... 计算逻辑
# 启动内核
grid = (N, M // BLOCK_SIZE)
kernel_gpu[grid](x, output, N, M, BLOCK_SIZE=128)
优化后的NPU版本(1D网格):
@triton.jit
def kernel_npu(x_ptr, output_ptr, N, M, BLOCK_SIZE: tl.constexpr,
m_step: tl.constexpr, task_num: tl.constexpr, num_core: tl.constexpr):
core_id = tl.program_id(0)
for task_id in tl.range(core_id, task_num, num_core):
pid_n = task_id // m_step
pid_m = task_id % m_step
# 计算偏移
x = x_ptr + pid_n * M + pid_m * BLOCK_SIZE
# ... 计算逻辑(所有代码都在循环内部)
# 启动内核
num_core = get_npu_properties()["num_vectorcore"]
m_step = M // BLOCK_SIZE
task_num = N * m_step
grid = (num_core,)
kernel_npu[grid](x, output, N, M, BLOCK_SIZE=128,
m_step=m_step, task_num=task_num, num_core=num_core)
本技能采用NPU专用迁移策略,将GPU实现迁移为NPU实现。
xxx_optimized.py或xxx_npu.py)_npu后缀错误示例:
# ❌ 不要添加_npu后缀
def _layer_norm_fwd_1pass_kernel_npu(...):
...
正确示例:
# ✅ 保持原始函数名
def _layer_norm_fwd_1pass_kernel(...):
# NPU optimized implementation
...
| 中文注释 | 英文注释 |
|---------|---------|
| # NPU优化版本 | # NPU optimized version |
| # NPU支持 | # NPU support |
| # NPU任务分发参数 | # NPU task dispatch parameters |
| # 使用1D物理核心网格 | # Use 1D physical core grid |
| # 从task_id重建原始索引 | # Reconstruct original indices from task_id |
| # 计算指针偏移 | # Calculate pointer offsets |
| # 获取设备属性 | # Get device properties |
原始GPU代码(layernorm_gated.py):
def _layer_norm_fwd_1pass_kernel(...):
# GPU kernel implementation
row = tl.program_id(0)
group = tl.program_id(1)
...
def _layer_norm_fwd(...):
grid = (M, ngroups)
with torch.cuda.device(x.device.index):
_layer_norm_fwd_1pass_kernel[grid](...)
优化后NPU代码(layernorm_gated_optimized.py):
# NPU support
import torch_npu
import triton.runtime.driver as driver
def get_npu_properties():
"""Get NPU device properties, including number of cores"""
device = torch.npu.current_device()
return driver.active.utils.get_device_properties(device)
def _layer_norm_fwd_1pass_kernel(...,
# NPU task dispatch parameters
ngroups_step: tl.constexpr,
task_num: tl.constexpr,
num_core: tl.constexpr,
):
# NPU optimization: Use 1D physical core grid with task dispatch
core_id = tl.program_id(0)
for task_id in tl.range(core_id, task_num, num_core):
# Reconstruct original 2D indices from task_id
row = task_id // ngroups_step
group = task_id % ngroups_step
# Calculate pointer offsets
X_ptr = X + row * stride_x_row + group * N
# ... kernel logic
def _layer_norm_fwd(...):
# NPU optimization: Use 1D physical core grid
npu_props = get_npu_properties()
num_core = npu_props["num_vectorcore"]
grid = (num_core,)
_layer_norm_fwd_1pass_kernel[grid](...)
在生成NPU专用代码时,确保:
_npu后缀)with torch.cuda.device(...)上下文num_vectorcore而非multi_processor_count(num_core,)ngroups_step, task_num, num_core)torch.cuda.is_available() 替换为 torch.npu.is_available()torch.cuda.empty_cache() 替换为 torch.npu.empty_cache()torch.cuda.synchronize() 替换为 torch.npu.synchronize()torch.cuda.mem_get_info() 替换为 torch.npu.mem_get_info()device="cuda" 替换为 device="npu"@torch.compile 装饰器testing
Kubernetes 集群健康检查与安全修复 — 诊断问题,用户确认后执行修复
tools
昇腾NPU CANN Toolkit+Kernels+NNAL安装部署技能。支持从官网下载run包安装和从Docker镜像提取两种方式,覆盖驱动检查、包下载、安装、环境变量配置与验证全流程。当用户需要安装CANN全套组件或指定版本CANN到自定义路径时调用。
development
编译 ATB (Ascend Transformer Boost) 测试框架。当用户需要编译 ATB 测试框架、 运行 CSV 测试、或构建 atb_test_framework 时调用。支持全量编译(含第三方依赖克隆与源替换) 和增量编译两种模式。需在 Docker 容器内配合 CANN 环境执行。
databases
ATB OPS→ACLNN 迁移标准化工作流主模板。整合前置学习、设计文档生成、CSV用例设计、 实际迁移、编译验证、测试验证全流程,提供明确的阶段 Gates 和用户确认机制。