PyPTO-Gym 算子设计文档规范:基于 pypto-pro-op-design 模板的 R0–R8 迭代式方案设计
2026/9/20 12:38:43 网站建设 项目流程
  • 人工智能
  • 大模型
  • 算子库
  • AI 技能/插件

【免费下载链接】pypto-gym

PyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库

项目地址:https://gitcode.com/cann/pypto-gym
点击查看免费下载

导读

本文是 CANN/pypto-gym 仓库中pypto-pro-op-design技能模板(design-template.md)的完整技术解读。该模板是 PyPTO-Pro 算子设计中「产出 DESIGN.md」的权威骨架:它以 R0–R8 共 9 轮迭代式约束收敛为核心,从 Module 划分、API 映射、Tile 规划、片上空间布局,到循环/Section 结构、分核策略、跨核同步、尾块处理、目标测试 case 与综合评估,最终汇总为一张可供 coder 直接施工的 Tile 数据流全景图。读完本文,你将掌握这套模板每一个章节(§0–§10)的填写规则、与之配套的 SKILL.md 迭代流程,以及DESIGN_BINDINGS.jsonmodule_interfaces.yaml等机器可读交付物的合同约束,能够独立为任意 PyPTO-Pro 算子产出「结论 + 推导过程 + 证据来源」三要素齐全、可直接翻译为 kernel 代码的设计文档。


一、模板的定位:从 SPEC 到 kernel 的「施工合同」

在 PyPTO-Pro 算子开发流程中,DESIGN.md位于资料探索(EXPLORE_REPORT.mdPRO_MATERIAL_INDEX.md)与 kernel 实现(pypto-pro-op-develop)之间,是设计验收通过后的实现合同。模板首页即声明了设计的输入来源:

  • 基于 SPECcustom/{op}/SPEC.md——公式、shape、dtype、动态轴等算子规格;
  • 基于 EXPLORE_REPORTcustom/{op}/EXPLORE_REPORT.md——API 映射与约束(§3)、相似样例(§4)、教程指导(§5)、Tile/同步策略建议(§6)、环境常量快照(§7,如 UB 容量、event_id 上限、对齐要求)。

模板开头的Knowledge Bindings一节明确了设计与知识库的绑定方式:完整、机器可读的 requirement 清单见DESIGN_BINDINGS.json(模板对应物为 design-bindings-template.json),而 DESIGN.md 正文只记录 R0–R8 的实际设计决策,不复制 Binding 分组或 requirement 表格,每条活动 requirement 的planned_location必须准确指向对应决策及最终test_<op>.py的 file/symbol。

模板通过 10 个章节(§0–§10)对应 9 轮迭代(R0–R8,其中 R7.5 专管测试 case 规划),概览如下:

章节输出轮次核心问题
§0 Module 划分R0计算流拆成几个 Module?各自属于哪个 Section?
§1 API 映射R1每个数学步骤用哪些 API?数值安全边界如何?
§2 Tile 规划R2每个 tile 的 shape/dtype/内存空间/layout/缓冲深度?
§3 片上空间布局R3tile 落在哪个片上地址?容量与对齐是否满足?
§4 循环与 Section 结构R4Module 如何放入 section,循环如何嵌套?
§5 分核策略R5work item 如何分配到各物理核?
§6 核间同步R6Cube/Vector 跨执行域依赖如何同步?
§7 尾块处理R7维度不整除 tile 尺寸时怎么办?
§8 目标测试 caseR7.5交给 develop 验证的具体 shape 是哪几个?
§9 综合评估R8整体设计是否准确、可靠、泛化?
§10 Tile 数据流全景图R8 之后全部数据流的单页全景

设计是问题驱动的迭代,不是线性填表。SKILL.md 的核心原则强调:每个决策必须包含结论 + 推导过程 + 证据来源;每轮发现的矛盾必须回溯修正前序决策,不允许累积到 R8 再处理;运行证据推翻设计时须返回design_violation,由编排器重新调度设计修订。


二、§0 Module 划分(R0):先数据依赖,再 Section 边界

2.1 维度契约——所有后续轮次的前提

模板要求在设计任何 Module 之前先确认 kernel 的输入/输出维度契约(不属于 Module 划分本身):按 SPEC 明确 kernel 接收的真实 rank、shape、stride 和输出形态。SPEC 要求支持 1D 或任意多维时,必须设计 kernel 内的索引/stride 映射,不能让 host 通过 reshape 等张量操作把输入归一化,也不能默默只实现 2D。模板以表格形式记录适配规则:

SPEC 输入 shapekernel 接收kernel 内索引/stride 映射输出形态
1D[L]原始[L]直接按 L 索引,不做 host reshapeSPEC 要求形态
2D[M,N]原始[M,N]按原始 stride/offset 访问SPEC 要求形态

2.2 划分依据与 Module 列表

Module 是设计文档中对计算流程的逻辑划分,不是 PyPTO Pro 的语法结构(见 module_partitioning.md)。划分分两步:

第一步,按数据依赖确定边界。对于相邻两步 A 和 B:

  • B 必须等 A 遍历完完整归约轴或其他完整数据范围、并基于 A 的最终结果开始新的遍历或独立计算阶段时 → 拆成两个 Module;
  • A 处理完当前 Tile 后 B 能在同一层循环内立即消费,且两步属于同一 Section → 通常放同一 Module;
  • A、B 之间只有少量同域计算,没有插入其他 Section 或独立计算阶段 → 可放同一 Module;
  • B 只是紧接着对 A 的归约结果做汇总或简单处理,不需要重新遍历完整数据范围 → 可留在 A 所在的 Module。

以稳定 Softmax(沿 N 轴分块)为例,常规实现需要三次完整遍历,后两次都依赖前一次得到的最终结果,因此按三次遍历划分:Module 1 遍历全部 N Tile 求每行m = max(x);Module 2 重新遍历求s = sum(exp(x - m));Module 3 再遍历写出y = exp(x - m) / s。采用 Online Softmax 时可在一次遍历中同时维护ms,通过m_new = max(m, tile_max)s_new = s * exp(m - m_new) + sum(exp(tile - m_new))更新,最终缩减为两个 Module。模板中还强调:A 的结果是否需要保存不能单独决定 Module 边界——即使 A、B 可以连续执行,若合并后 UB 等片上空间放不下同时存活的数据,也必须拆分。

第二步,按 Section 边界继续拆分。一个 Module 只能属于一个 Section:L1/L0A/L0B/L0C 上的矩阵路径(含 GM→L1、L1→L0A/L0B、矩阵乘、L0C 写回)归 Cube Section;Vec(UB)上的搬运和向量计算归 Vector Section(使用 VF 时外层 Kernel 负责 GM↔UB 搬运,@pl.vector_function负责 UB↔寄存器交换与寄存器计算);pl.quant/pl.dequant走 V 流水,归 Vector Section;pl.move/pl.store对 Acc 结果做随路量化/反量化时走 FIX 流水,归 Cube Section。模板特别提醒:Scaling只是量化参数使用的片上缓冲区,不是独立 Section,不能看到MemorySpace.Scaling就新建 Module。

2.3 归约轴容量结论与 Module 级数据流

模板要求含归约算子时必填「归约轴是否可能超单 tile」:若动态轴范围可能跨多个 tile,须写明多 Tile 归约方案(常规多遍 / 在线统计加输出两遍 / 其他),逐遍写明遍历范围、状态和依据;单 tile 装得下则写「不涉及」。最后用 ASCII 示意图标注 Module 间的数据流向、中间数据所在空间、传递 API 及 Cube/Vector 之间的同步方式。

R0 的机器可读产物是module_interfaces.yaml(single source of truth),包含module_countis_fusion(同时含 cube 和 vec section →true,隐含module_count >= 2)、has_cross_coremodules[](id/name/description/section/golden_steps/inputs/outputs/golden_stage_fn)、final_outputscomposition_verification(atol/rtol/seeds/shapes)。骨架由脚本生成,见 gen_module_interfaces.py:

python ./scripts/gen_module_interfaces.py custom/<op>/<op>_golden_cpu.py \ --spec custom/<op>/SPEC.md --op <op> \ --design custom/<op>/DESIGN.md > custom/<op>/module_interfaces.yaml

脚本自动解析 golden 函数签名并填充schema_version/op/primary_inputs/composition_verification,architect 负责填写标TODO的判断部分,产出后用 validate_module_yaml.py 自验。


三、§1 API 映射(R1):冻结每个 Vector 步骤的唯一实现

3.1 数学步骤到 API 调用链

API 映射从数学公式出发,但公式不能直接决定 Kernel 写法(详见 api_mapping_and_tile_planning.md)。首先确定每一步由 Cube 还是 Vector 执行,再确定 Tile 的 shape、dtype、内存空间与 layout。以稳定 Softmax 为例,核心数据依赖为:

x ── reduce_max(N) ── max │ │ └──────── subtract ── exp ── reduce_sum(N) ── sum │ exp ───────── divide ── out

Cube 路径受硬件数据通路硬性约束:pl.matmul(dst, lhs, rhs)要求lhs位于 Left(L0A)、rhs位于 Right(L0B)、dst位于 Acc(L0C),一次矩阵乘通常展开为GM → Mat(L1) → Left/Right(L0A/L0B) → matmul/matmul_acc → Acc(L0C) → store/move → GM 或 UB。VF 路径则是GM → pl.load → Vec(UB) → vf.load* → Register File → vf.* → vf.store* → Vec(UB) → pl.store → GM

模板要求按$CANNBOT_CONFIG_ROOT/references/performance-constraints.md中的「强制 2:Vector 默认使用 VF」为每个 Vector 步骤冻结唯一实现,并记录vector_selection字段:

vector_selection: step: <本 §1 中的哪一步> implementation: vf | tile_op api_sequence: <vf.* 或 pl.* API 序列> decision_reason: default_vf | kb_template_required kb_template_evidence: <default_vf 写 n/a;tile_op 写已选 KB 路径、明确要求的原文/片段> target_version: <目标软件/框架版本> applicable_conditions: <dtype/shape/layout/算子条件>

随后按 Module 写出 API 调用序列(含每个 API 的来源:API 文档路径或 EXPLORE_REPORT §4 定位的官方指定算子)。数学公式通常省略搬运与表示转换,API 调用链还必须补充:GM/L1/L0/UB/寄存器之间的搬运、dtype/layout 转换与归约结果广播、动态尾块使用的set_validshape/VF mask/fillpad、Cube 与 Vector 之间传递中间结果所需的数据通路(GM workspace 或move支持的片上通路)。

3.2 超越函数数值安全边界

模板条件性要求:仅当 API 链含 exp/log/sqrt/reciprocal/tanh 等超越/非线性函数时,填写数值安全边界表,例如:

API输入理论范围目标 dtype 上限是否溢出防护措施
expx 无上界,x>11 时 exp 溢出fp16 ≈ 65504exp(11.09)≈65504 → +inf → NaN先减最大值再 exp(或归一化省去分母)

防护措施必须来自算子定义或明确规格,不能为了通过测试给普通除法擅自加入epsilon或改变边界语义。模板还强调窄 dtype(fp16/bf16)下平方、同量级相乘、长轴累加的中间值量级上界分析:若超出范围,升位宽的vf.astype应落在产生增长的那一步之前而非归约之前。


四、§2 Tile 规划(R2):关键常量、Tile 属性与槽位访问

4.1 关键常量定义

模板要求列出所有编译期常量,coder 必须逐字使用,涉及 Cube 的 tile 尺寸须满足 EXPLORE_REPORT §7 记录的对齐要求:

# ── 算子关键常量(coder 必须逐字使用) ── TS = {S_tile} # S 方向 tile 尺寸 TD = {D_tile} # D 方向 tile 尺寸 TS_HALF = {S_half} # subblock 半尺寸 (如有 dual_split) SCALE = 1.0 / sqrt({D_logical}) # 缩放因子(若算子有 scale 步骤)

关键注意:若某维度实际值固定且小于 tile 尺寸(如 D=64 对齐到 TD=128),tile shape 用 TD 声明,运行时通过 §7 的set_validshape限制有效区域;公式常量(如 SCALE)必须使用实际维度值而非 tile 尺寸。

4.2 Tile 属性表与 TileGroup 槽位访问

每个 tile 的 shape 由其所参与的 API 操作数要求决定,模板的 Tile 属性表覆盖:用途、变量名、shape、dtype、内存空间(Vec(UB)/Mat(L1)/Left(L0A)/Right(L0B)/Acc(L0C))、layout、大小、备注。

TileGroup 槽位访问表要求记录depth和逐 Tile 的 mutex 配置,避免漏掉「单 Tile 多 ID」以及「不配置 mutex 元数据」的情况。运行时下标不会自动取模,采用group[i]时必须给出i始终位于[0, depth)的依据。模板还条件性要求填写tile_dimsstride 注意事项:仅当使用load_tile+tile_dims=[d0, d1](覆盖多维度)时填写,大 stride 可能影响性能。

4.3 make_tile_group 与 make_tile 的分工

这是两条实现约束之一(设计阶段须在 R3 落实):所有需要 buffer 切换/轮转的 tile 一律用make_tile_group+auto_mutexmake_tile仅限单次使用 scratch tile(写入一次、读取一次,不参与轮转,不跨循环迭代保留)。禁止用make_tile+ 手动sync_src/sync_dst管理 buffer 轮转。

make_tile_group的槽位数由depth确定,mutex_ids描述每个 Tile 携带的 mutex ID。参考 api_mapping_and_tile_planning.md 中的配置示例:

配置对应的 Tile 数每个 Tile 的 mutex ID
mutex_ids=[0]1Tile 0:[0]
mutex_ids=[0, 1]2Tile 0:[0];Tile 1:[1]
mutex_ids=[[0, 1]]1Tile 0:[0, 1](单 Tile 多 ID)
mutex_ids=[[0, 2], [1, 3]]2Tile 0:[0, 2];Tile 1:[1, 3]

注意:省略depth时必须提供非空mutex_ids(框架用其长度推导槽位数);mutex_ids=None[]时无法推导槽位数,必须填写depth。TileGroup 的访问方式分为四类:next()(游标前进一格)、current()(游标不变)、previous()(取前一槽位)、group[i](不读取也不修改游标)。显式下标适合「同一轮同时指明当前槽位和预取槽位」「生产者与消费者按同一slot_idx访问共享缓冲」等场景;group[i]next()混用时两套游标状态要分别推导。


五、§3 片上空间布局(R3):逐空间独立寻址、精确到字节

5.1 地址映射表与最高地址上界

每个 MemorySpace独立寻址、独立限容——地址各自从0x00000起算,同一 addr 在不同空间是不同物理位置(L0A/L0B/L0C 同用addr=0x0000互不冲突)。模板按内存空间分节填写地址映射表,地址统一使用半开区间[起始字节, 结束字节),生命周期重叠的 tile 地址不得相交。含 cube/matmul 的算子须补 L1(Mat)/L0A(Left)/L0B(Right)/L0C(Acc) 各节。

各空间容量结论须给出百分比:{max(结束字节(不含))} / {EXPLORE_REPORT §7 对应容量},容量取 §7 探测记录,§7 未记录则回退 material-explore 补测,不臆测。按照 onchip_memory_layout.md,各 MemorySpace 的编译期地址对齐要求为:Vec/Mat/ScaleLeft/ScaleRight32B,Left/Right512B,Acc64B。TileGroup 使用单个基地址时,第i个槽位按slot[i].addr = base + i × slot_size展开,须检查每个base + i × slot_size而不是只查base

5.2 地址复用条件与专项检查

两个逻辑 Tile 只有同时满足以下条件才能复用地址:位于同一 MemorySpace 且区间能容纳完整物理大小;生命周期不重叠或同步保证新写入发生在旧数据最后一次读取之后;核内跨 Pipe 复用具有正确的 mutex 关系或显式核内同步;跨 Cube/Vector 复用具有 R6 定义的 READY/RELEASE 同步。mutex_id是同步元数据,不负责分配地址也不检查容量,取值范围[0, 31]

R3 还需执行专项检查:MX scale 地址按ScaleLeftAddr[i] = LeftAddr[i] >> 4ScaleRightAddr[i] = RightAddr[i] >> 4计算(独立逻辑地址域,不从数据空间扣容量);对计划并行访问的 Mat Tile 检查 L1 Bank 冲突;A5 的 1:2 混合 Kernel 中两个 AIV 各自拥有本地 Vec 地址域,UB 分别检查、不能相加比较。


六、§4 循环与 Section 结构(R4):从结果单元确定循环层次

6.1 Module 放入 Section 与结果单元

R4 依据 loop_design.md 把 §0 确定的 Module 放入具体 Section 代码结构:相邻同域 Module 可以顺序写在同一个 Section 中;Cube Module 和 Vector Module 分别放入pl.section_cube()pl.section_vector();两个 Section 共享的 TileGroup 在 Section 之前声明。若 API 数据通路与 §0 记录的 Section 归属冲突,回到 R0 修正 Module 划分。

确定循环层次的关键是先确定结果单元——循环一次要完成的输出。例如:逐元素算子以一个输出 Tile 为结果单元;沿 N 轴 Softmax 以一行块为结果单元(行内所有 N Tile 属于同一归约);Matmul 以[M_tile, N_tile]输出块为结果单元(所有 K Tile 共同更新其 Acc 状态)。

6.2 循环嵌套、动态上界与分核信息

模板要求记录:Module/Section 对应关系、结果单元、跨 Tile 状态生命周期(初始化位置、更新方式、使用位置)、Section 结构(section_vector/section_cube的数量与顺序、循环相对 Section 的位置)、循环嵌套(参照样例说明 M-tile/N-tile 关系)、动态循环上界(如n_tiles = (N + TILE_N - 1) // TILE_N;静态 shape 写固定值及来源)、分核信息(核编号、核数及获取位置)。两种典型循环形式:

# 扁平任务循环:二维输出 Tile 坐标展平成一维任务编号 for task_id in pl.range(core_idx, m_tiles * n_tiles, core_num): m_idx = task_id // n_tiles n_idx = task_id % n_tiles ... # 行/列两层循环:同一行的多个 Tile 共享数据或状态 for m_idx in pl.range(core_idx, m_tiles, core_num): for n_idx in pl.range(0, n_tiles, 1): ...

分核信息取决于 Section 所在执行域:仅 Cube/仅 Vector 用pl.get_block_idx()/pl.get_block_num();混合 Kernel 的 Vector Section 按全部 AIV 分工时,core_num 为pl.get_block_num() * pl.get_subblock_num()pl.get_block_idx()返回展平后的全局 AIV 核编号。模板要求填写「主要参考样例」(EXPLORE_REPORT §4 定位的官方指定算子路径)、可复用结构点与补充参考(official_samples.md 清单)。

6.3 CV 并行流水设计(is_fusion=true时必填)

模板指出当前框架提供自动 CV 并行流水功能,须读取$PYPTO_DEVKIT_DIR/docs/guide/programming_guide/pro/advanced_programming/auto_parallel_pipeline.md并逐项核对「使用约束」:全部满足时推荐自动流水;任一不满足时记录具体条款,并按 cv_fusion_pipeline.md 采用手动流水。流水结构必须在编排 Stage 3 冻结,不得留到 Stage 5 才引入。需填写的内容包括:流水实现(auto/manual及约束核对结果)、自动流水配置(stage 函数及顺序、跨核 TileGroup 的fwd_ids/bwd_ids、串行精度验证配置、候选与初始preloadpipeline_generated.py复核位置)、流水编号(第一阶段每次交给下一阶段的数据范围、task_id递增位置)、阶段链、逐交接的 1:2 分工、阶段延迟表、上下文循环缓冲、预加载轮数与缓冲深度、流水启动/稳定运行/末尾剩余阶段、槽位初始可写状态。

手动流水的核心是任务错位:Cube 和 Vector 在同一轮处理不同编号的任务,同一任务内部仍按原有数据依赖顺序执行。四阶段QK(Cube) → P(Vector) → PV(Cube) → GU(Vector)样例的阶段延迟为0/1/2/3;采用 3 轮预加载时,第 3 轮起进入稳定运行段,四个阶段分别处理QK(3)P(2)PV(1)GU(0),最后一个真实任务后 Cube 再运行 2 轮、Vector 再运行 3 轮完成剩余阶段。通用多阶段流水的延迟计算规则为:第一阶段delay = 0;某执行域第一次出现delay = 上一阶段 delay + 1;同一执行域再次出现delay = 同执行域上一阶段 delay + preload。上下文循环缓冲深度取max_delay + 1


七、§5 分核策略(R5):恰好 1 次 launch 合同

R5 的权威依据是$PYPTO_DEVKIT_DIR/docs/guide/programming_guide/pro/development/tile_based_python_programming/multi_core_partitioning_and_Tiling.md。模板要求填写:

  • 分核方式:strided loop——扁平切pl.range(core_id, m_tiles*n_tiles, num_cores)或二维切(外range(core_id, m_tiles, num_cores)+ 内range(0, n_tiles, 1))+ 选择理由;
  • host 侧 block_dim:仅 Vector Kernel 使用vector_core_num,Cube 或混合 Kernel 使用core_numtotal_tasks > 0block_dim = min(max_blocks, total_tasks)total_tasks = 0时仅允许经目标验证的block_dim=1空工作单次启动,否则报design_violation禁止block_dim=0或跳过启动
  • 交付 launch 合同:恰好 1 次;唯一 kernel{kernel_symbol}{wrapper_symbol}在 host 循环外调用一次;若做不到,记录证据并返回failure_category: design_violation,不得填写多 launch fallback;
  • TensorList ABI(不涉及则 N/A):冻结合同中的有限L_MAXB_MAX=L_MAX、每槽有限元素数上限N_MAX对所有DT_INT32派生式的安全性、只生成一组B_MAX个固定槽位、每个 TensorList 形参逐槽展开为独立 Ptr 参数、wrapper 对真实槽位校验和n_i=0填充、地址值不进入 tiling 数据;
  • A5 CV 的两个 Vector subblock 分工:切分轴、各自的索引范围、尾块归属和工作量;
  • 两个 Vector subblock 的结果依赖:无依赖可独立处理;存在归约依赖时说明为何不能改切非归约轴、GM 部分结果的 shape 与地址、最终合并者。

模板特别强调 TensorList 类算子的per-tensor launch是「最常见也最贵的设计错误」:基线是一次融合调用,逐 tensor launch 会把融合消除的开销又加回来。


八、§6 核间同步(R6):READY/RELEASE 双向协议与 event_id 分配

8.1 cross_core 涉及判定

R6 是条件性章节:仅当 Cube 与 Vector 之间有数据依赖,或存在需要INTER_BLOCKINTER_SUBBLOCKUNICAST_BLOCK处理的依赖时填写;否则填「不涉及 cross_core」。Section 数量本身不是判断依据。判定流程见 cross_core_synchronization.md:消费者读取本次 Kernel 中由另一执行域或另一 Block/subblock 写入的数据时需要 cross-core 同步;同执行域内依赖由带非空mutex_ids的 TileGroup 配合auto_mutex管理,未配置时手工插入核内同步(sync_src/sync_dst),VF 函数内局部读写顺序用vf.mem_bar,配置matmul phase时 M 与 FIX 之间由unit_flag配对。

8.2 同步点、事件表与 event_id

模板的同步点表按数据方向和物理槽位记录就绪/释放事件:READY为生产者→消费者(set 在最后一次写后、wait 在第一次读前),RELEASE为消费者→生产者(set 在最后一次读后、wait 在下一次覆盖前)。pipe表示执行 set/wait 的硬件流水(不表示 Section 名称),取值由同步点紧邻的数据操作决定:Cube 将 Acc 写入 GM workspace、Vector 再从 GM 加载到 UB 时用FIX → MTE2;Cube 将 Acc 搬到 Vec 后由 Vector 计算时用FIX → V;Vector 通过 MTE3 写入 Mat、Cube 再搬入 L0 时用MTE3 → MTE1

event_id 分配表要求:取值范围[0, 16),动态表达式的全部运行时取值也必须在范围内;一对 set 和 wait 必须使用相同的 ID 与sync_mode;前一个信号由配对 wait 消费后 ID 才可以复用。sync_mode语义:AIC 与两个 AIV 子核协同用INTRA_BLOCK;两个 AIV 子核之间用INTER_SUBBLOCK;跨物理 block 用INTER_BLOCK;只与一个 AIV 子核同步用UNICAST_BLOCKevent_idmutex_id是两套独立机制,数值相同不会自动建立联系,也不能相互替代。

手动同步的设计关系(模板要求引用):slot_idx = task_idx % depthtile = shared_group[slot_idx]READY = READY_IDS[slot_idx]RELEASE = RELEASE_IDS[slot_idx]。循环复用缓冲时,Vector 在主循环前为每个空槽位预发 RELEASE 事件表示初始可写;每轮 Cube 写完后 set READY、Vector 读完后 set RELEASE、Cube 下一次覆盖前 wait RELEASE。模板还要求检查事件配对:每个 wait 必须对应同一方向、同一槽位、同一轮次的 set;所有执行路径都必须保证配对 set 最终执行,等待关系不能形成环;覆盖零次、一次、整除和尾块迭代中的事件配对。


九、§7 尾块处理(R7):物理 shape 固定、有效 shape 随块变化

R7 的权威依据是$PYPTO_DEVKIT_DIR/docs/guide/programming_guide/pro/development/tile_based_python_programming/tail_block_handling.md,核心模型是:Tile 的物理 shape 固定,有效 shape 随当前块变化。模板要求填入以下代码:

# ceiling division 计算 tile 数(必须向上取整,用 N//TILE 直接整除会漏掉尾块) m_tile_num = (M + TS - 1) // TS n_tile_num = (N + TD - 1) // TD # 循环内计算尾块有效尺寸并告知硬件(set_validshape 有状态,每轮都要重设) valid_m = pl.min(M - m_off, TS) # 满 tile = TS, 尾块 = 余数 valid_n = pl.min(N - n_off, TD) pl.set_validshape(tile_a, [valid_m, valid_n]) # 运行时告知硬件

还需填写:compactNone/1/2,按数据通路和 API 约束说明依据;普通 Vec ND 尾块通常不需要);无效区域是否会被读取(否 → 不填充;是 → 按计算语义选择pad并执行fillpad;矩阵尾块是否填充由具体数据路径和 API 约束决定)。注意fillpad当前只支持 Vec Tile,不能用于 Mat/Left/Right 操作数。


十、§8 目标测试 case(R7.5):基于 tile 切分的具体 shape

R7.5 要求基于 R2 tile 尺寸与 R7 尾块方案确定具体测试 shape,develop 直接实现这些 case,不再自行重算。若 SPEC/用户已有目标 case 在其基础上追加;无用户指定 case 时直接确定,不询问用户。至少 4 个基础 case(单轴算子按例外处理并说明原因):

case 名具体 shape覆盖场景design 适配确认
test_{op}_aligned[TILE_A, TILE_B]全整除✅ / 不适配→回溯轮次
test_{op}_tail[TILE_A + 尾块余数, TILE_B]单轴尾块✅ / ...
test_{op}_tail2d[TILE_A + 尾块余数, TILE_B - 尾块余数]双轴尾块✅ / ...
test_{op}_multitile[2~3 × TILE_A + 尾块余数, TILE_B - 尾块余数]跨多 tile + 尾块(最小规模,勿放大)✅ / ...

关键原则:逐个验证 design 可适配;若某 case 适配不了,必须回溯对应轮次具体解决(尾块缺陷回 R7、循环边界错误回 R4、归约轴超单 tile 回 R0),不得反过来删改 case 迁就 design;若算子凑不出 4 个有区分度的 case,以覆盖到的边界类别为准,不为凑数量制造冗余用例。


十一、§9 综合评估(R8):三张检查表 + 迭代规则

R8 综合评估从三个维度对 R0–R7 的全部输出做交叉验证,orchestrator 以评估表中无 ❌ 作为设计通过判据

准确性检查:API 调用链完整覆盖数学公式;§4 已参照官方样例确定循环与 Section 结构(含样例路径与结构说明);数据依赖正确(Module 顺序 + sync 点);dtype 精度满足要求(如 FP32 matmul 累加);归约类 API 的[M,1]/[1,N]输出已设layout;Acc tile 物理 M×N×dtype_bytes ≥ fractal(FP32 ≥ 1024 bytes;动态轴含极小维度时尤需检查)。

泛化性检查:目标测试 case(≥4)已按 tile 切分确定具体 shape 且逐个验证;尾块处理正确(ceiling division + set_validshape);归约轴超单 tile 时已给出覆盖完整归约轴的方案;循环边界正确;超越函数在 dtype 范围内无溢出;跨 tile 状态初始化/持久化正确;cross_core 同步方案正确。

一致性检查:R0–R7 输出无矛盾;所有决策有证据支撑;R3 片上地址范围与逐空间容量检查通过;tile_dims使用时已关注大 stride 影响;条件性检查(若 §6 填「不涉及 cross_core」则确认不存在 Cube↔Vector 或跨 Block/subblock 的数据依赖)。

迭代规则(来自 SKILL.md):发现问题数 ≤ 3,修复后重新走 R8;问题数 > 3,回到问题最早出现的轮次重新过后续轮;最多 5 次完整 R0–R8 迭代。评估结论记录「整体:通过/需修改」「限制条件」(如不支持尾块、需 M 整除完整 M-tile 尺寸等)与「修改记录」(修改内容、轮次、原因)。


十二、§10 Tile 数据流全景图:给 coder 的施工合同

R8 通过后,将 R0–R7 各轮产出串联为一张完整的 tile 级数据流图,标注每一块 tile 的流向:从哪里读取、经过哪些操作转换、写入哪里。模板给出了绘图骨架:

{按实际算子的 tile 流向绘制} GM ─[load_tile]→ tile_a ─→ {操作} → {输出tile} → ... ↓ {操作}({输出tile}, {持久化tile}) ↓ {持久化tile} → tile_a → {操作} → ... → tile_out ↓ GM ←[store_tile]─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘

图例约定:[load_tile]为 MTE2 搬运(GM → UB);[store_tile]为 MTE3 搬运(UB → GM);为 V 流水线操作并标注 API 名;---表示数据跨 Module 持久化(tile 在不同 Module 间不被覆盖);├─表示同一 Module 内分支(同一数据被多次使用)。验证规则:全景图中每一块 tile 和每一个操作都必须能在 R3 的地址映射表、R1 的 API 序列中找到对应条目,缺失或矛盾则回溯修正。SKILL.md 明确:Tile 数据流图是给 coder 的施工合同——coder 拿到 DESIGN.md 应能确定 kernel 的完整结构与关键决策;API 签名等细节以 API 文档原文为准确认,EXPLORE_REPORT 仅为派生的先行速查,不作签名权威。


十三、配套交付物与强制规则

13.1 DESIGN_BINDINGS.json 结构合同

除了 DESIGN.md,设计阶段还产出custom/<op>/DESIGN_BINDINGS.json。按 design-bindings-template.json 与 SKILL.md 的合同:根对象含schema_version(整数1)与bindings(数组);Binding 含class_idselection_field(只能是optional_patternsrequired_constraints)、referenceselection_reasonrequirements(非空数组);requirement 含req_idkindsource_anchorsstatusclass_evidenceinvariantplanned_locationverification_methodkind + status只允许precondition + metobligation + appliesobligation + not_triggeredvalidation_scope + applies四种组合,其中obligation + applies是活动项,每个 Binding 至少一条,其invariantplanned_location为非空字符串。DESIGN.md 只链接 JSON,不复制 Binding 表。

13.2 wrapper 边界强制规则

模板中的「Wrapper 边界外操作」必须填写。按照 SKILL.md 与 wrapper-boundary.md:cast、slice、transpose、pad、concat 以及任何数据形状/dtype 处理必须放进@pl.jitkernel 内部;wrapper 只做参数校验、读取 KB 约束列明的只读元数据、纯 Python 整数推导、torch.empty分配输出和一次 kernel 启动。模板给出反面样例——一次 kernel 启动外面包了四个被计时的 device 算子(.to(torch.float32)movedim().contiguous()等),kernel 被写成只接受「规范化的 FP32 连续 2-D 输入」,于是 host 被迫去生产它,「这份便利按全价计费」。设计阶段必须:kernel 接口按真实输入定义(原始 dtype、原始 layout、原始 rank);需要的轴变换用 stride/offset 索引在 tile 循环里表达,不用movedim/permute/contiguous;dtype 转换在 tile load/store 时用pl.cast(tile 级)或vf.astype(寄存器级)完成,不在 host 上.to()整个张量(没有vf.cast这个 API);尾块用pl.set_validshape,不在 host 上 pad 到 tile 整数倍。注意方向:wrapper 也不得承担算子的算术(实现验收的 anti-cheat 会判 FAIL),要求是「形状/dtype 处理进 kernel」,不是「计算搬到 host」。


十四、总结与使用建议

design-template.md作为 PyPTO-Pro 算子设计的权威模板,其价值在于把「可执行的 kernel 方案」强制拆解为 10 个可独立验证、可交叉回溯的设计决策章节:R0 定 Module 边界与维度契约,R1 冻结 API 链与数值边界,R2 定 tile 规格与缓冲深度,R3 落到精确到字节的片上地址,R4 组织循环与 Section,R5 定分核与单次 launch 合同,R6 设计跨核 READY/RELEASE 协议,R7 处理尾块,R7.5 冻结测试 case,R8 用三张检查表做验收,最后以 §10 全景图作为 coder 的施工合同。配合 SKILL.md 的迭代流程、references 目录下的六份专题设计指南(module_partitioning.md、api_mapping_and_tile_planning.md、onchip_memory_layout.md、loop_design.md、cross_core_synchronization.md、cv_fusion_pipeline.md),以及gen_module_interfaces.pyvalidate_module_yaml.pytest_module_contract.py等校验脚本,这套模板构成了从 SPEC 到 kernel 的完整设计证据链。实际使用时,建议:进入 R0 前先读取performance-constraints.md落实两条实现约束;每个决策优先参考 EXPLORE_REPORT §4 定位的官方指定算子(复用成熟模式拥有最高优先级);严格遵循「结论 + 推导过程 + 证据来源」三要素,任何无法落到代码的决策都应标记为待回溯项,直到 R8 评估表全绿。

  • 人工智能
  • 大模型
  • 算子库
  • AI 技能/插件

【免费下载链接】pypto-gym

PyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库

项目地址:https://gitcode.com/cann/pypto-gym
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询