pypto-gym 中的 linspace 组合实现:arange + mul 逐 tile 生成等差序列的 NPU kernel 骨架
【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym
本文以 linspace.md 这篇 kernel 参考骨架为主体,讲清torch.linspace在 PyPTO 框架下的组合实现方案:为什么 linspace 不是原子接口、arange/full/mul/assemble四个 API 如何拼出等差序列、"无 batch 轴时沿输出轴 tile loop 切分"这一模式的设计动机,以及骨架中各占位符的含义与适用边界。读完后你能够照骨架落地一个生成类算子的 kernel 草稿,并知道它距离可上 NPU 还需要经过哪些验证环节。
1. 背景:linspace 在 Torch ↔ Pypto 映射表中的定位
pypto-gym 的 cannbot-skills/ops/pypto-api-explore/references/torch-pypto-op-mapping.md 维护了一张 Torch 与 Pypto 的对标表。其中「同名映射」部分明确列出:Pypto 的创建类原子接口只有arange、full、ones、zeros(见该文件第 19 行"创建"分组),linspace并不在原子接口之列,因此被归入「组合方案」:
| Torch 算子 | Pypto 组合方案 | 参考实现 |
|---|---|---|
linspace | arange+mul | linspace.md |
对照同组的eye(arange+one_hot+cast,见 eye.md),可以看出 PyPTO 处理"生成类"算子的思路是一致的:不直接造数,而是用 arange 生成索引序列,再经逐元素运算与写入操作组合出目标数据。linspace 的目标序列out[i] = start + i * (end - start) / (n - 1)中,start为 0 的典型情形(如模型里生成0..num_grid_per_side-1的网格坐标)就退化为i * step,恰好只需要arange+mul两步,这就是组合方案表里只写两个 API 的原因。
2. 参考骨架:完整代码与逐行解读
原骨架给出的 kernel 如下(来自 linspace.md),文档中的 Note 一句话点明了切分方式:
无 batch 轴,沿输出轴按 tile loop 切分;每 tile 生成一段等差序列。
@pypto.frontend.jit(runtime_options={"run_mode": pypto.RunMode.NPU}) def linspace_kernel(out: pypto.Tensor(ol, pypto_dtype)): for i in pypto.loop(num_tiles, name="out_tile", unroll_list=[1]): step = pypto.full([tile_len], 1.0 / max(n - 1, 1), pypto_dtype) idx = pypto.cast(pypto.arange(tile_len), pypto_dtype) seg = pypto.mul(idx, step) pypto.assemble(seg, [i * tile_len], out)2.1 接口签名:只有一个输出张量
函数签名out: pypto.Tensor(ol, pypto_dtype)说明本算子没有输入张量,ol(输出 shape 列表)与pypto_dtype(元素 dtype)都是外部预先算好的量。这一结论与 examples 目录的占位符约定一致:examples/README.md 定义了全部骨架共用的占位符,与本骨架直接相关的是:
| 占位符 | 含义 |
|---|---|
ol | 输出 shape 列表 |
pypto_dtype | 元素 dtype,如pypto.DT_FP32 |
n | 输出总长度(linspace 的元素个数) |
num_tiles/tile_len | 生成类沿输出轴切 tile 的分块数 / 每块长度(linspace 等) |
生成类算子的所有"输入"其实都是标量参数(起止值、元素个数 n),shape 本身由 host 侧确定,这正是骨架里没有输入pypto.Tensor参数的原因。
2.2 tile loop:输出轴切分的动机与写法
pypto.loop(num_tiles, name="out_tile", unroll_list=[1])沿输出轴把整个等差序列切成num_tiles段、每段tile_len个元素(即n = num_tiles * tile_len)。切分的收益在 README 的约定里有明确说法:骨架要展示"哪些轴 loop、哪些轴整块"的接口组合模式。对生成类算子而言,逐 tile 生成可以控制单次 kernel 迭代的工作量与 UB(统一缓冲)占用,避免一次性把整个大张量放进片上处理;同时 loop 内所有计算只依赖i(当前 tile 序号)和常量,host 侧即可完全确定,不存在数据依赖的动态轴问题。
unroll_list=[1]表示该循环以 1 为步长、不做循环展开,与同目录其它骨架(如 cat.md 的unroll_list=[1])保持一致;具体tile_len取多少、要不要展开,README 已声明需"按实际 shape / dtype 与平台约束确定并调优"。
2.3 tile 内三步计算:full → arange+cast → mul
每轮迭代生成第i段序列,写法上有三个值得注意的细节:
step = pypto.full([tile_len], 1.0 / max(n - 1, 1), pypto_dtype):步长1/(n-1)是 host 侧标量,这里用pypto.full把它物化成长度为tile_len的常量张量。max(n - 1, 1)是显式的除零防护:n == 1时(退化输出只有一个元素)分母取 1 而非 0,与torch.linspace在numel=1时不再除步长的行为对齐。idx = pypto.cast(pypto.arange(tile_len), pypto_dtype):arange生成的是整数索引序列0..tile_len-1(创建类原子接口,见 torch-pypto-op-mapping.md 第 19 行),随后cast到目标 dtype 再参与乘法,避免整数乘浮点步长时的类型问题。seg = pypto.mul(idx, step):等差序列即"索引 × 步长"。骨架对应的是 start=0 的通用形态;若 start 非 0,按组合方案再补一次add(逐元素add属同名同参原子接口)即可。
这里体现的是 PyPTO 生成类 kernel 的通用数据流:arange 负责"位置",full 负责"常量",逐元素运算负责"取值",assemble 负责"落位"。
2.4 assemble 落位:元数据式写入
pypto.assemble(seg, [i * tile_len], out)把当前 tile 写到输出张量偏移i * tile_len处。assemble在 PyPTO 中是"view 取片 + assemble 拼回"的搬运语义——同目录 cat.md 的 Note 对这类算子有同样的定性:本算子为 metadata/搬运语义(view 取片 + assemble 拼回),无逐元素计算。linspace 的 loop 内只有构造与写入,没有跨张量归约,因此多 tile 之间互不干扰,不需要 pypto-specific-ops.md 中atomic_add一类的多核合并手段。从源码结构看,仓库内生产实现也大量使用assemble按 tile 偏移写回输出,例如 deepseek_v2_lite_chat/mla_prolog/mla_prolog.py 中按[tIdx, 0, 0]偏移分段 assemble,以及 deepseek_v32_exp/lightning_indexer_quant_impl.py 中按s1_tile_idx * s1_tile偏移写回,与本骨架i * tile_len的偏移模式是同一套路。
3. 与同类骨架的横向对照:何时用 batch loop,何时用输出轴 tile loop
examples 目录下每个<op>.md都遵循 README 的 Note 约定:用一句话说明切分方式,且"无 batch-row loop 的骨架(cube、sort、生成/索引类)须说明实际采用的切分方式"。linspace 正属于"生成类",与 eye.md 的对照能说明选轴逻辑:
- eye:有隐含的行轴(输出第 0 轴),沿行 loop,每行整块生成 one-hot;
- linspace:输出只有一维,没有 batch 轴可借,只能沿唯一的输出轴自己切 tile;
- cat:有显式 batch 轴(
sl[0]),沿 batch loop 搬运。
即 loop 轴的选取原则是:有 batch 轴优先 loop batch;纯生成/索引类则沿输出轴切 tile。这一点也解释了骨架中num_tiles、tile_len这对占位符只出现在"生成类沿输出轴切 tile"的场景(见占位符表第 18 行),而不是所有骨架通用。
4. 生产模型里的真实需求:linspace 用在哪
骨架对应的现实需求可以在仓库的 transformers 代码中直接找到。以 qwen3_5_9b/modeling_qwen3_5.py 为例,视觉塔插值模块按grid_thw生成归一化网格坐标:
for _, h, w in grid_thw_list: h_idxs = torch.linspace(0, self.num_grid_per_side - 1, h) w_idxs = torch.linspace(0, self.num_grid_per_side - 1, w) h_idxs_floor = h_idxs.int() w_idxs_floor = w_idxs.int()qwen3_6_27b 的 modeling 文件 中有完全相同的用法。这类调用的特点是:
- start 恒为 0,终点是"网格边长减一",元素个数 h/w 由输入图片决定——正是骨架里 start=0 形态、
n由 host 侧标量给出的形态; - 序列长度不大,但属于"无输入张量、纯参数驱动"的生成类算子,切分维度天然只有输出轴。
从源码结构看,这两处目前仍直接调用torch.linspace;PyPTO 侧要承接该语义,组合方案(arange + full + mul + tile loop + assemble)就是现成的实现路径。
5. 使用骨架的正确姿势:探索流程、门禁与验证边界
5.1 在 API 探索工作流中的位置
linspace.md 属于 pypto-api-explore 技能包的一部分。按该 SKILL 的工作流,命中「组合方案」条目后应:
- 按映射表取用组合方案(linspace →
arange+mul),并阅读 examples 下对应骨架; - 占位符约定以 examples/README.md 为准,README 给出了最小可运行 setup 示例(
B, D = 8, 128等); - 命中条目后仍需通过 Explore 核实具体约束与生产实现,再按 templates/api_report.md 生成含「API 映射 / 约束 / Tiling / 风险评估 / 证据索引」等章节的报告,门禁要求 6 个章节非空。
5.2 骨架的边界声明(必须遵守)
README 对 examples 目录有两条硬声明,引用骨架时不能越过:
- 骨架"仅展示接口组合与轴切分模式……不作为标准模板:loop 轴、
unroll_list、tile shape、动态轴处理等需按实际 shape / dtype 与平台约束确定并调优"; - "所有骨架未逐一经 NPU 编译验证"。
因此正确流程是:以骨架为草稿 → 按实际n/dtype 设定num_tiles、tile_len(并确认 tile 数与 tile 长度符合 set_vec_tile_shapes 相关 Tiling 约束 中"每维 > 0、最多 4 维"等硬约束)→ 走仓库内 pypto-op-verify、pypto-kernel-validate 等技能包定义的测试流程做精度比对后再使用。此外注意run_mode: pypto.RunMode.NPU表明该骨架面向 NPU 运行模式;骨架中1.0 / max(n - 1, 1)的 host 侧除法要求n在编译期已知,n为动态值时需要在风险评估中按 SKILL 文档"动态 shape 兼容性"一节标注并调整切分策略。
6. 小结
| 要点 | 结论 | 依据 |
|---|---|---|
| linspace 无原子接口 | 归入「组合方案」:arange+mul | torch-pypto-op-mapping.md |
| 切分模式 | 无 batch 轴,沿输出轴 tile loop,num_tiles × tile_len = n | linspace.md |
| 除零防护 | max(n - 1, 1)处理n == 1退化情形 | 骨架源码 |
| 落位方式 | assemble(seg, [i * tile_len], out),搬运语义、tile 间无数据竞争 | 骨架源码及 cat.md 同类 Note |
| 生产对应场景 | ViT 网格坐标生成(start=0 形态) | modeling_qwen3_5.py |
| 使用边界 | 骨架非标准模板、未逐一经 NPU 编译验证,需按实际约束调优并走验证流程 | examples/README.md |
一句话概括本文主线:linspace 在 PyPTO 中"组合实现 = arange 造索引、full 造步长、mul 出序列、tile loop 控规模、assemble 按i * tile_len落位",骨架给出的是接口组合与切分模式的最小表达,实际落地前需按占位符约定填参、按 SKILL 门禁核实约束、按验证流程完成精度比对。
【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考