skills/tilelang-vector-ascend-ops-migration/SKILL.md
# TileLang GPU到NPU算子迁移 Skill ## 技能概述 本skill用于指导TileLang算子从GPU(CUDA)平台迁移到华为昇腾NPU平台。通过分析GPU实现,自动生成对应的NPU实现代码。 ## 适用场景 - 将`gpu/`目录下的TileLang算子迁移到`npu/`目录(或者说讲适配GPU的tilelang算子迁移为适配NPU的tilelang算子,如果用户没有说迁移到的算子放在哪里,就新建一个npu目录,放在npu目录下,并提示用户迁移算子的存放位置) - 自动适配NPU硬件约束和API差异 - 生成可直接运行的NPU算子代码 ## 📚 重要参考文档 **在迁移过程中,GPU 和 NPU 的 API 接口存在显著差异,强烈建议参考以下文档:** ### 核心文档 - **`docs/GPU-To-NPU-Migration-Methods.md`** - 详细迁移经验和案例 ### 补充文档 - **`references/debugging-guide.md`** - NPU算子调试指南 - 精度问题、编译失败、运行时错误的调
npx skillsauth add Ascend/agent-skills skills/tilelang-vector-ascend-ops-migrationInstall 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.
本skill用于指导TileLang算子从GPU(CUDA)平台迁移到华为昇腾NPU平台。通过分析GPU实现,自动生成对应的NPU实现代码。
gpu/目录下的TileLang算子迁移到npu/目录(或者说讲适配GPU的tilelang算子迁移为适配NPU的tilelang算子,如果用户没有说迁移到的算子放在哪里,就新建一个npu目录,放在npu目录下,并提示用户迁移算子的存放位置)在迁移过程中,GPU 和 NPU 的 API 接口存在显著差异,强烈建议参考以下文档:
docs/GPU-To-NPU-Migration-Methods.md - 详细迁移经验和案例references/debugging-guide.md - NPU算子调试指南
references/hardware-optimization.md - NPU硬件特性与性能优化
使用建议:
TileLang-NPU-API-Reference.mdT.vconv → T.vcast)debugging-guide.mdhardware-optimization.md最常见错误1:使用逐元素处理而非分块处理
这是 NPU 迁移中最致命且最容易犯的错误。NPU 的向量化架构要求使用分块处理,而不是逐元素处理。
# ❌ 错误写法:逐元素处理(效率极低且容易出错)
@T.prim_func
def main(X: T.Tensor((M, N), "float16"), Y: T.Tensor((M, N), "float16")):
with T.Kernel(M * N, is_npu=True) as (cid, _):
idx = cid
bx = idx // N
by = idx % N
# 为每个元素分配独立的 buffer(错误!)
x_ub = T.alloc_shared((1,), "float16")
y_ub = T.alloc_shared((1,), "float32")
# 单个元素访问(错误!)
T.copy(X[bx, by], x_ub)
T.vsigmoid(x_ub, y_ub)
T.copy(y_ub, Y[bx, by])
# ✅ 正确写法:分块处理
@T.prim_func
def main(X: T.Tensor((M, N), "float16"), Y: T.Tensor((M, N), "float16")):
with T.Kernel(T.ceildiv(M, block_M) * T.ceildiv(N, block_N), is_npu=True) as (cid, _):
grid_y = T.ceildiv(N, block_N)
bx = cid // grid_y
by = cid % grid_y
# 为整个 block 分配 buffer
x_ub = T.alloc_shared((block_M, block_N), "float16")
y_ub = T.alloc_shared((block_M, block_N), "float32")
# 使用切片访问整个 block
T.copy(X[bx * block_M : (bx + 1) * block_M,
by * block_N : (by + 1) * block_N], x_ub)
T.vsigmoid(x_ub, y_ub)
T.copy(y_ub, Y[bx * block_M : (bx + 1) * block_M,
by * block_N : (by + 1) * block_N])
为什么必须使用分块处理:
推荐的 block 大小:
block_M=64, block_N=64block_size=256 或 block_size=512最常见错误2:Grid 大小计算位置错误
在迁移过程中,另一个最容易犯且最高频的错误是在 @T.prim_func 内部定义局部变量来计算 grid 大小。
# ❌ 错误写法(会导致编译失败)
@T.prim_func
def main(...):
num_blocks = M * N # 局部变量
with T.Kernel(num_blocks, is_npu=True): # 编译时找不到 num_blocks!
...
# ✅ 正确写法
@T.prim_func
def main(...):
with T.Kernel(M * N, is_npu=True): # 直接使用参数计算
...
原因: T.Kernel 的 grid 参数在编译阶段求值,此时函数内部的局部变量还不存在。
解决方案: 始终在 T.Kernel(...) 参数中直接使用函数参数和 T.ceildiv 计算,不要使用任何局部变量。
最常见错误3:使用不存在的标量运算 API
NPU 不支持标量与向量直接运算的 API(如 T.vmuls、T.vadds 等),必须先将标量广播为向量。
# ❌ 错误写法:使用不存在的 T.vmuls
alpha_scalar = T.cast(0.01, "float32")
T.vmuls(x_fp32, alpha_scalar, result) # API 不存在!
# ✅ 正确写法:先广播再向量运算
alpha_scalar = T.cast(0.01, "float32")
alpha_ub = T.alloc_shared((block_M, block_N), "float32")
T.vbrc(alpha_scalar, alpha_ub) # 广播标量到向量
T.vmul(x_fp32, alpha_ub, result) # 向量乘法
关键点:
T.vadd、T.vmul、T.vsub、T.vdivT.vadds、T.vmuls 等都不存在T.vbrc(scalar, tensor) 将标量广播成向量T.vbrc 的第一个参数必须是变量,不能是字面量最常见错误4:T.vcmp 参数顺序错误
NPU 的 T.vcmp API 需要显式指定输出 buffer,且参数顺序与 GPU 不同。
# ❌ 错误写法:返回值方式(GPU 风格)
mask = T.vcmp(x_fp32, zero_ub, "GT") # NPU 不支持!
# ❌ 错误写法:参数顺序错误
T.vcmp(x_fp32, zero_ub, "gt", mask_ub) # 参数顺序错误!
# ✅ 正确写法:显式输出 buffer
mask_ub = T.alloc_shared((block_M, block_N), "bool")
T.vcmp(x_fp32, zero_ub, mask_ub, "gt") # 正确顺序
T.vselect(mask_ub, true_val, false_val, result)
关键点:
T.vcmp 的正确签名:T.vcmp(src0, src1, dst, cmp_op)"bool" 类型的输出 buffer"gt", "lt", "ge", "le", "eq", "ne"None,结果存储在 dst 参数中最常见错误5:T.copy 用于广播操作
NPU 的 T.copy 不支持形状不匹配的复制,不能用于广播操作。必须使用 T.vbrc 进行标量广播。
# ❌ 错误写法:使用 T.copy 广播
max_ub = T.alloc_shared((1, 1), "float32") # 归约结果
T.reduce_max(x_fp32, max_ub, dim=-1)
max_brc = T.alloc_shared((1, N), "float32")
T.copy(max_ub, max_brc) # ❌ 错误:形状不匹配 (1,1) → (1,N)
# ✅ 正确写法:提取标量后使用 T.vbrc 广播
max_ub = T.alloc_shared((1, 1), "float32")
T.reduce_max(x_fp32, max_ub, dim=-1)
max_brc = T.alloc_shared((1, N), "float32")
max_scalar = max_ub[0, 0] # 提取标量值
T.vbrc(max_scalar, max_brc) # 广播到向量
关键点:
T.copy 要求源和目标形状完全一致,不支持自动广播T.reduce_max、T.reduce_sum)的结果通常需要广播回原始维度[0, 0])提取标量值T.vbrc(scalar, tensor) 将标量广播到整个 tensor最常见错误6:分块处理时忽略边界检查
这是 NPU 分块处理中非常容易遗漏的问题。当 tensor 维度不是 block 大小的整数倍时,最后一个 block 会访问越界内存,导致运行时崩溃。
# ❌ 错误写法:忽略边界检查
@T.prim_func
def main(X: T.Tensor((M, N), "float16"), Y: T.Tensor((M, N), "float16")):
with T.Kernel(T.ceildiv(M, block_M) * T.ceildiv(N, block_N), is_npu=True) as (cid, _):
grid_y = T.ceildiv(N, block_N)
bx = cid // grid_y
by = cid % grid_y
x_ub = T.alloc_shared((block_M, block_N), "float16")
y_ub = T.alloc_shared((block_M, block_N), "float16")
# 当 M 或 N 不是 64 的倍数时,最后一个 block 会越界!
T.copy(X[bx * block_M : (bx + 1) * block_M, by * block_N : (by + 1) * block_N], x_ub)
T.vrelu(x_ub, y_ub)
T.copy(y_ub, Y[bx * block_M : (bx + 1) * block_M, by * block_N : (by + 1) * block_N])
# ✅ 正确写法:使用 size 参数处理边界
@T.prim_func
def main(X: T.Tensor((M, N), "float16"), Y: T.Tensor((M, N), "float16")):
with T.Kernel(T.ceildiv(M, block_M) * T.ceildiv(N, block_N), is_npu=True) as (cid, _):
grid_y = T.ceildiv(N, block_N)
bx = cid // grid_y
by = cid % grid_y
row_start = bx * block_M
col_start = by * block_N
# 计算当前 block 的实际大小
actual_M = T.min(block_M, M - row_start)
actual_N = T.min(block_N, N - col_start)
x_ub = T.alloc_shared((block_M, block_N), "float16")
y_ub = T.alloc_shared((block_M, block_N), "float16")
# 使用 size 参数指定实际拷贝大小
T.copy(X[row_start, col_start], x_ub, size=[actual_M, actual_N])
T.vrelu(x_ub, y_ub)
T.copy(y_ub, Y[row_start, col_start], size=[actual_M, actual_N])
关键点:
actual_M = T.min(block_M, M - row_start)T.copy(src, dst, size=[actual_M, actual_N])T.copy(X[start:end, ...], dst, size=[...]) 是错误的T.copy(X[row_start, col_start], dst, size=[actual_M, actual_N])错误现象:
The write address of the MTE instruction is out of range
vector core exception
最常见错误7:T.copy 切片语法与 size 参数冲突
TileLang 不允许同时使用切片语法和 size 参数,这会导致编译错误。
# ❌ 错误写法:切片语法 + size 参数
T.copy(X[row_start:row_end, col_start:col_end], x_ub, size=[actual_M, actual_N])
# 错误信息:T.copy: cannot use both slice syntax and the size parameter.
# ✅ 正确写法:起始位置索引 + size 参数
T.copy(X[row_start, col_start], x_ub, size=[actual_M, actual_N])
记忆口诀:
使用
size参数时,源 tensor 只能用起始位置索引(X[row, col]),不能用切片语法(X[start:end])。
最常见错误8:测试数据超出实际模型范围
这是一个极易被忽视但影响重大的错误。NPU的底层API经过针对性优化(如使用近似函数拟合),只在特定数值范围内保证精度。
# ❌ 错误写法:生成超出实际范围的测试数据
def test_sigmoid_npu():
# sigmoid在实际模型中输入通常在[-10, 10]范围
X = torch.randn(M, N) * 100 # 生成[-300, 300],超出实际范围!
Y = sigmoid_npu(X)
# 可能导致精度测试失败,但实际模型中不会出现这种输入
# ✅ 正确写法:符合实际模型场景的测试数据
def test_sigmoid_npu():
# 分析实际模型中sigmoid的输入分布
X = torch.randn(M, N) * 3 # 生成[-9, 9]范围,符合实际
Y = sigmoid_npu(X)
关键原则:
为什么这很重要:
必须使用分块处理(Block-based Processing):NPU 算子必须采用分块处理方式,不能逐元素处理。每个 kernel 处理一个 block(如 64x64)的数据,使用切片访问内存。
必须处理边界情况(Boundary Handling):当 tensor 维度不是 block 大小的整数倍时,必须使用 size 参数处理边界。计算实际大小:actual_M = T.min(block_M, M - row_start),并在 T.copy 中使用 size=[actual_M, actual_N]。
查阅 NPU API 文档确认所有 API 存在:迁移前必须在 ./docs/TileLang-NPU-API-Reference.md 中确认每个使用的 API 都存在。特别注意:
T.vmuls、T.vadds 等不存在)T.vbrc 广播后再用向量 APIT.vcast 而非 T.vconvT.vcmp 必须显式分配 "bool" 类型的输出 buffer,参数顺序为 T.vcmp(src0, src1, dst, cmp_op)T.copy 不支持形状不匹配的复制,归约结果广播必须用 T.vbrc测试数据必须符合实际模型场景:生成测试用例时,输入数据的范围必须符合实际模型中可能出现的情况。NPU的底层API经过针对性优化(如近似函数拟合),只在特定数值范围内保证精度。
关键原则:
错误示例:
# ❌ 错误:sigmoid在模型中输入通常有界,不会出现极大值
X = torch.randn(M, N) * 100 # 生成[-300, 300]范围,不符合实际
# ✅ 正确:符合实际模型中sigmoid的输入范围
X = torch.randn(M, N) * 3 # 生成[-9, 9]范围,符合实际
拒绝迁移需要使用已经发现精度问题的 API 的算子
如果需要使用这些API,要么想办法绕过去,用其他替代,如果不能替代就直接报告无法迁移
拒绝使用的API
暂空
当用户请求迁移某个算子时,首先读取GPU实现文件:
gpu/<算子名称>.py
分析以下关键信息:
参考docs/TileLang-NPU-API-Reference.md和docs/GPU-To-NPU-Migration-Methods.md,应用以下核心迁移规则:
GPU版本:
import tilelang as tl
import tilelang.language as T
@tl.jit(pass_configs={...})
def kernel_impl(...):
NPU版本:
import os
import tilelang
import tilelang.language as T
os.environ['TILELANG_ASCEND_MODE'] = 'Developer'
@tilelang.jit(target="npuir")
def kernel_impl(...):
| GPU | NPU |
| ------------------- | -------------------- |
| T.bfloat16 | "float16" |
| T.float32 | "float32" |
| T.int32 | "int32" |
| T.dynamic("name") | T.symbolic("name") |
GPU版本:
with T.Kernel(T.ceildiv(seq_len, block_Q), threads=512) as bx:
tx = T.thread_binding(0, 512, thread="threadIdx.x")
NPU版本:
with T.Kernel(num_blocks, is_npu=True) as (cid, _):
# cid是core ID,NPU自动管理线程
| 操作 | GPU | NPU |
| ---------- | ------------------------ | -------------------------------- |
| 清零 | T.fill(tensor, 0) | T.clear(tensor) |
| GEMM初始化 | clear_accum=True | initC=True |
| GEMM转置 | transpose_A=True | a_transpose=True |
| 原子操作 | T.atomic_add(dst, src) | T.atomic_add(dst, src, [size]) |
在T.Parallel内:
T.vmax/T.vmin替代T.max/T.minA[i+1])T.vselect替代条件判断GPU版本(循环):
for i, j in T.Parallel(M, N):
C[i, j] = T.max(A[i, j], 0) * B[i, j]
NPU版本(向量化):
T.copy(A, A_ub)
T.vrelu(A_ub, A_ub)
T.vmul(A_ub, B_ub, C_ub)
💡 详细优化指南:本节提供基本的硬件约束检查要点。更深入的性能优化策略(包括Tiling策略、存算并行、分核优化等)请参考
references/hardware-optimization.md
硬约束: 单个Kernel内所有T.alloc_shared()和T.alloc_fragment()的总和必须 < 96KB
建议值: < 85KB(预留临时变量空间)
内存计算公式:
# FP16: 2 bytes, FP32: 4 bytes, INT32: 4 bytes
total_bytes = sum(shape[0] * shape[1] * dtype_bytes for each allocation)
示例:
# ✅ 安全(约48KB)
q_shared = T.alloc_shared([64, 128], "float16") # 16KB
k_shared = T.alloc_shared([64, 128], "float16") # 16KB
acc_ub = T.alloc_shared([64, 64], "float32") # 16KB
# ❌ 超限(256KB)
q_shared = T.alloc_shared([512, 128], "float16") # 128KB
k_shared = T.alloc_shared([512, 128], "float16") # 128KB
如果内存超限,需要:
.contiguous()Cube后Vector操作卡死:T.gemm()后立即使用T.vcmp+T.vselect会卡死
T.vrelu替代Gather后GEMM崩溃:循环内Gather数据后直接GEMM会编译失败
CV分核Bug:某些情况需要添加无意义操作触发优化
T.vadd(k_shared, T.cast(0.0, dtype), k_shared)生成的NPU代码应包含以下结构:
import os
import torch
import tilelang
import tilelang.language as T
os.environ['TILELANG_ASCEND_MODE'] = 'Developer'
FP16 = "float16"
FP32 = "float32"
INT32 = "int32"
@tilelang.jit(target="npuir")
def _kernel_impl(...):
dtype = FP16
accum_dtype = FP32
# 使用T.symbolic定义动态shape
seq_len = T.symbolic("seq_len")
@T.prim_func
def kernel(...):
with T.Kernel(num_blocks, is_npu=True) as (cid, _):
# 1. 内存分配
# 2. 数据加载
# 3. 计算逻辑
# 4. 结果写回
return kernel
def wrapper_function(...):
# PyTorch接口封装
kernel = _kernel_impl(...)
kernel(...)
return output
def run_test():
# 测试代码,对比参考实现
pass
if __name__ == "__main__":
run_test()
💡 调试指南:如果测试失败或遇到编译/运行时错误,请参考
references/debugging-guide.md获取详细的调试方法。
生成代码后,执行以下命令测试:
:warning:调试之前,请先询问用户,tilelang-ascend相关环境是否配好
python3 <迁移的算子名称、路径>.py"
必须包含以下内容:
标准测试代码模板:
def test_xxx_npu():
# 1. 准备输入数据
X = torch.randn(..., dtype=torch.float16, device="npu").contiguous()
Y_npu = torch.empty(..., dtype=torch.float16, device="npu")
# 2. 运行 NPU kernel
kernel = xxx_npu(...)
kernel(X, Y_npu)
# 3. 运行 PyTorch 参考实现
Y_ref = pytorch_reference(X)
# 4. 计算误差
max_diff = torch.max(torch.abs(Y_npu - Y_ref)).item()
print(f"Max difference: {max_diff}")
# 5. 判断并输出结果(必须)
if max_diff < 1e-3:
print("✅ Validation Passed!")
else:
print("❌ Validation Failed.")
if __name__ == "__main__":
test_xxx_npu()
关键要求:
Max difference: xxx✅ Validation Passed! 或 ❌ Validation Failed.max_diff < 1e-3.contiguous()执行迁移时,必须检查以下项目:
block_M, block_N 参数,不能逐元素处理T.ceildiv(M, block_M) * T.ceildiv(N, block_N)actual_M = T.min(block_M, M - row_start),使用 size 参数处理非整数倍边界T.copy(X[row_start, col_start], dst, size=[...]) 而非切片语法配合 size(block_M, block_N) 而非 (1,)os.environ['TILELANG_ASCEND_MODE'] = 'Developer'@tilelang.jit(target="npuir")is_npu=TrueT.bfloat16 → "float16"T.float32 → "float32"T.int32 → "int32"T.dynamic → T.symbolic.contiguous()T.fill(x, 0) → T.clear(x)clear_accum → initCtranspose_A/B → a_transpose/b_transposeT.max/T.min → T.vmax/T.vmin(在T.Parallel内)T.atomic_add(dst, src) → T.atomic_add(dst, src, [size])T.vcmp 使用正确参数顺序:T.vcmp(src0, src1, dst, cmp_op),需先分配 "bool" 类型的 dst buffer迁移过程中请参考:
docs/TileLang-NPU-API-Reference.md - NPU API简略文档(包含所有API但内容简略)docs/GPU-To-NPU-Migration-Methods.md - 详细迁移经验docs/examples下,每个python文件都是一个简单vector tilelang算子从GPU迁移到NPU的示例错误现象:
DeprecationWarning: T.Buffer(...) is deprecated, use T.Tensor(...) instead
解决方案:
# ❌ 错误
X: T.Buffer((M, N), "float16")
# ✅ 正确
X: T.Tensor((M, N), "float16")
错误现象:
error: 'hivm.hir.vmul' op requires the same element type for all operands
原因:
NPU的向量操作(T.vmul, T.vadd等)要求所有操作数类型完全一致,不能混用FP16和FP32。
解决方案:
# ❌ 错误:混用FP16和FP32
x1_ub = T.alloc_shared((M, N), "float16")
y_ub = T.alloc_shared((M, N), "float32")
T.vmul(x1_ub, y_ub, y_ub) # 类型不匹配!
# ✅ 正确:先转换类型
x1_ub = T.alloc_shared((M, N), "float16")
x1_fp32 = T.alloc_shared((M, N), "float32")
y_ub = T.alloc_shared((M, N), "float32")
T.vcast(x1_ub, x1_fp32) # FP16 → FP32
T.vmul(x1_fp32, y_ub, y_ub) # 类型一致
错误现象:
error: module 'tilelang.language' has no attribute 'vconv'
原因:
NPU不支持T.vconv,应使用T.vcast进行类型转换。
解决方案:
# ❌ 错误
T.vconv(x_fp16, "none", x_fp32, 1.0)
# ✅ 正确
T.vcast(x_fp16, x_fp32)
错误现象:
ValueError: Failed to evaluate grid expression 'num_blocks': name 'num_blocks' is not defined
ValueError: Failed to evaluate grid expression 'total_tokens': name 'total_tokens' is not defined
根本原因:
TileLang 编译器在处理 T.Kernel(grid_size, ...) 时,会在编译阶段尝试解析 grid_size 表达式。此时 @T.prim_func 内部定义的局部变量尚未执行,因此无法被访问。
技术细节:
T.Kernel 的 grid 参数必须是编译时可求值的表达式M, N, batch_size)和 TileLang 内置函数(如 T.ceildiv)@T.prim_func 内部的任何局部变量解决方案:
方案1:2D Grid 展平
# ❌ 错误示例1
@T.prim_func
def main(...):
grid_x = (M + block_M - 1) // block_M
grid_y = (N + block_N - 1) // block_N
num_blocks = grid_x * grid_y
with T.Kernel(num_blocks, is_npu=True) as (cid, _): # 编译时 num_blocks 不存在!
...
# ✅ 正确:直接在 Kernel 参数中计算
@T.prim_func
def main(...):
with T.Kernel(T.ceildiv(M, block_M) * T.ceildiv(N, block_N), is_npu=True) as (cid, _):
grid_y = T.ceildiv(N, block_N)
bx = cid // grid_y
by = cid % grid_y
...
方案2:简单乘法
# ❌ 错误示例2
@T.prim_func
def main(...):
total_tokens = batch_size * seq_len # 局部变量
with T.Kernel(total_tokens, is_npu=True) as (cid, _): # 编译时 total_tokens 不存在!
...
# ✅ 正确:直接使用参数计算
@T.prim_func
def main(...):
with T.Kernel(batch_size * seq_len, is_npu=True) as (cid, _):
b = cid // seq_len
s = cid % seq_len
...
记忆口诀:
T.Kernel 的 grid 参数中,只能直接使用函数参数和 T.ceildiv,不能使用任何局部变量。
问题: 计算Swish等复合激活函数时,需要注意中间结果的复用。
最佳实践:
# Swish(x1) * x2 = (x1 * sigmoid(x1)) * x2
T.vcast(x1_ub, x1_fp32) # 转FP32
T.vcast(x2_ub, x2_fp32)
T.vsigmoid(x1_fp32, y_ub) # sigmoid(x1) → y_ub
T.vmul(x1_fp32, y_ub, y_ub) # x1 * sigmoid(x1) → y_ub
T.vmul(y_ub, x2_fp32, y_ub) # swish(x1) * x2 → y_ub
T.vcast(y_ub, y_fp16) # 转回FP16
错误现象:
The write address of the MTE instruction is out of range
vector core exception
原因: 当 tensor 维度(M 或 N)不是 block 大小(如 64)的整数倍时,最后一个 block 的切片访问会超出 tensor 边界,导致内存越界错误。
错误示例:
# ❌ 错误:当 M=100, block_M=64 时,第二个 block 会访问 [64:128],超出边界
T.copy(X[bx * block_M : (bx + 1) * block_M, by * block_N : (by + 1) * block_N], x_ub)
解决方案:
# ✅ 正确:计算实际大小并使用 size 参数
row_start = bx * block_M
col_start = by * block_N
actual_M = T.min(block_M, M - row_start) # 第二个 block: min(64, 100-64) = 36
actual_N = T.min(block_N, N - col_start)
T.copy(X[row_start, col_start], x_ub, size=[actual_M, actual_N])
关键点:
T.copy 中使用 size 参数限制实际访问范围size 参数不能同时使用错误现象:
error: T.copy: cannot use both slice syntax and the size parameter.
原因:
TileLang 编译器不允许 T.copy 同时使用切片语法和 size 参数。
错误示例:
# ❌ 错误:切片语法 + size 参数
T.copy(X[row_start:row_end, col_start:col_end], x_ub, size=[actual_M, actual_N])
解决方案:
# ✅ 正确:使用起始位置索引 + size 参数
T.copy(X[row_start, col_start], x_ub, size=[actual_M, actual_N])
记忆口诀:
需要边界处理时,
T.copy用起始位置索引配合size参数,不用切片语法。
当用户请求迁移算子时:
<gpu算子名称>.py文件<npu算子名称>.py文件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 和用户确认机制。