ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

PyPTO-Gym 算子设计 R0 阶段:Module 划分方法论与实战指南

PyPTO-Gym 算子设计 R0 阶段:Module 划分方法论与实战指南 PyPTO-Gym 算子设计 R0 阶段Module 划分方法论与实战指南【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gymModule 划分是 PyPTO-Pro 算子 tile 级方案设计pypto-pro-op-design中 R0 轮的核心任务在动手规划 Tile、循环与片上布局之前先把整体计算流拆成逻辑清晰、可独立验证的计算模块。本文以 module_partitioning.md 为骨架结合 PyPTO-Gym 仓库中的设计流程、脚本与模板系统讲解如何根据数据依赖与Section 边界完成 Module 划分并产出可供下游 R1–R8 轮直接消费的 Module 契约。读完本文你将掌握稳定 Softmax、Online Softmax、CubeVector 组合算子等典型场景的 Module 切分方法并能正确填写 R0 的 Module 列表与module_interfaces.yaml。Module 是什么逻辑划分不是语法结构Module 是设计文档中对计算流程的逻辑划分不是 PyPTO Pro 的语法结构。换句话说Module 只存在于 DESIGN.md 与module_interfaces.yaml这类设计产物中kernel 源码里并不存在名为 Module 的代码块。这一点决定了划分时的两个基本视角数据视角计算流中哪些步骤之间存在必须先完成 A 才能开始 B的依赖关系资源视角相邻步骤合并在同一个 Module 后UB 等片上空间能否放下同时存活的数据。Module 划分主要看数据依赖和Section 边界同时需要评估片上资源是否足够。整个 R0 轮在 pypto-pro-op-design 的九轮迭代设计R0–R8中承担分解计算流的职责其完整流程见 SKILL.md 的「R0Module 划分」一节而 R0 的产出将写入 design-template.md 的 §0「Module 划分R0 输出」以及机器可读的module_interfaces.yaml。Module 划分按两步进行根据数据依赖找出必须分开的计算步骤根据API 和数据通路判断 Section 边界Cube 和 Vector 计算属于不同 Section也要拆成不同 Module。下面分别展开这两步。第一步根据数据依赖确定边界对于相邻的两步计算 A 和 B是否拆成两个 Module可以按以下常见情况判断情形判断理由B 必须等 A 遍历完完整的归约轴或其他完整数据范围并基于 A 的最终结果开始一次新的遍历或独立计算阶段通常划分为两个 ModuleB 的输入是 A 的全局结果二者无法在同一轮循环内完成A 处理完当前 Tile 后B 能在同一层循环内立即消费该 Tile并且两步属于同一个 Section通常放在同一个 Module数据可在循环内直接接力无需跨遍历保存状态A 和 B 之间只有少量同域计算没有插入其他 Section 或独立计算阶段可以放在同一个 Module依赖紧、边界不明显拆分开反而增加设计复杂度B 只是紧接着对 A 得到的归约结果做汇总或简单处理不需要重新遍历完整数据范围可以继续放在 A 所在的 ModuleB 不依赖对完整数据范围的重遍历需要特别强调的是这些是常用判断方式不是硬性规则。有两个容易误解的点A 的结果是否需要保存不能单独决定 Module 边界。同一 Module 内也可能暂存中间结果例如跨 Tile 循环的累加状态不同 Module 之间也可能通过片上空间直接传递数据并不一定落 GM。即使 A 和 B 可以连续执行如果合并后 UB 等片上空间放不下同时存活的数据也需要拆分 Module。资源约束可以反向制造边界——这是也需要注意片上资源是否足够的落点。案例一沿 N 轴分块的稳定 Softmax三遍遍历以沿 N 轴分块的稳定 Softmax 为例。常规实现需要三次完整遍历后两次遍历都依赖前一次得到的最终结果因此通常按三次遍历划分三个 ModuleModule 1遍历全部 N Tile得到每行的 m max(x) Module 2重新遍历全部 N Tile得到每行的 s sum(exp(x - m)) Module 3重新遍历全部 N Tile写出 y exp(x - m) / s对同一个行块前一个 Module 遍历完全部 N Tile 后下一个 Module 才能开始。这里的边界来自基于最终结果重新遍历 N 轴这一数据依赖不是因为m或s需要保存——m、s作为跨 Module 的归约状态即使在同一个 Module 内部也需要持久化可见是否需要保存与是否拆分 Module是两个维度。该三遍结构与 api_mapping_and_tile_planning.md 中给出的稳定 Softmax 数据依赖图一致x → reduce_max(N) → max随后subtract → exp → reduce_sum(N) → sum最后exp ÷ divide → out。其中max、sum的 shape 均为[M, 1]后续计算需要沿 N 轴广播。案例二Online Softmax两遍遍历采用 Online Softmax 时可以在一次遍历中同时更新最大值和指数和。处理完前 k 个 N Tile 后每行维护两个跨 Tile 状态m当前最大值s以当前m为基准的指数和即sum(exp(x - m))。每次合入一个新 Tile 的更新公式为m_new max(m, tile_max) s_new s * exp(m - m_new) sum(exp(tile - m_new))由于最终的m和s到遍历结束时才能确定写输出时通常要重新读取各 Tile因此典型的 Module 划分是两遍Module 1在线遍历全部 N Tile得到最终 m 和 s Module 2重新遍历全部 N Tile写出 exp(x - m) / s这里有一个重要的边界细节如果后续只对最终的m或s做一次汇总或简单运算不再重新遍历 N 轴这部分可以留在 Module 1 中。这与根据数据依赖确定边界表格中第四条判断B 只是紧接着对 A 的归约结果做汇总可留在同一 Module完全对应。关于 Online Softmax 的完整递推与尾块处理仓库中的模式文档 online-softmax-tail.md 给出了更完整的公式含输出累加量o与数值安全要求状态m、l、o的累加应保持在 FP32尾块 mask 必须在row_max之前生效padding 零对 max 不是恒等元并且 PyPTO-Pro 中模块级float(inf)无法编译会渲染成未声明的 C 标识符inff应使用有限哨兵值NEG_LARGE -1.0e30。案例三Attention 的在线归一化——不能直接套用 Softmax 公式Attention 的在线归一化与 Online Softmax 的关键区别在于除了m和s它还要维护输出累加量。m更新时已有的输出累加量也要按新的m缩放再合入当前 Tile。因此Attention 应按完整的在线更新公式划分 Module不能直接套用上面的 Softmax 公式。完整的 Attention 在线更新递推见 online-softmax-tail.md 的 Complete recurrence为m_chunk row_max(s) m_new max(m_old, m_chunk) alpha exp((m_old - m_new) * scale) p exp((s - m_new) * scale) l_new l_old * alpha row_sum(p) o_new o_old * alpha p v_chunk最终y o_new / l_new。其中分母更新不可分割只乘alpha不加row_sum(p)是错误的。这条模式文档同时提示QK/PV 交接与 cross-core 同步属于目标版本相关的内容应从官方代码推导而不是从递推公式照搬——这与 Module 划分后由 R6 轮跨核同步承接的工作边界一致。第二步根据 Section 边界划分数据依赖分析完成后再根据API 使用的执行域、内存空间和硬件通路判断相邻步骤之间是否存在 Section 边界。Section 是 PyPTO Pro 中按执行域组织的代码结构pl.section_cube()/pl.section_vector()不同 Section 使用不同的硬件流水因此跨 Section 的数据交接天然形成 Module 边界。各数据通路对应的 Section 归属如下计算/搬运路径所属 Section说明L1/L0A/L0B/L0C 上的矩阵路径Cube Section包括 GM→L1、L1→L0A/L0B、矩阵乘和 L0C 写回VecUB上的数据搬运和向量计算Vector Section使用 VF 时外层 Kernel 负责 GM 与 UB 之间的搬运pl.vector_function负责 UB 与寄存器之间的数据交换和寄存器计算pl.quant和pl.dequantVector Section走 V 流水输入、scale、offset 和输出均为 Vec Tilepl.move/pl.store对 Acc 结果做随路量化或反量化Cube Section操作走 FIX 流水per-channel 参数使用MemorySpace.ScalingTile 时参数先从 MatL1搬到 Scaling再由 FIX 使用UB→L1、L0C→UB 等跨执行域搬运按当前move接口文档选择数据通路以目标版本 API 文档为准关于量化参数缓冲有一个易踩的坑Scaling是量化参数使用的片上缓冲区不是独立的 Section。不能看到MemorySpace.Scaling就新建 Module仍要根据使用它的 API 和数据通路判断属于 Cube 还是 Vector。同理pl.quant/pl.dequant因为走 V 流水放在 Vector Section而pl.move/pl.store对 Acc 的随路量化走 FIX 流水放在 Cube Section——同样是量化归属完全不同。一个 Module 只能属于一个 Section。相邻步骤分别属于 Cube 和 Vector 时在两者之间划分 Module。R0 只记录每个 Module 属于 Cube 还是 Vector具体使用几个 Section 代码块、多个同域 Module 是否放在同一个 Section以及循环相对 Section 的位置在 R4循环与 Section 结构确定。这符合 loop_design.md 的约定R0 已经记录每个 Module 属于 Cube 还是 VectorR4 据此确定源码中的 Section 结构相邻的同域 Module 可以顺序写在同一个 Section 中。案例四Cube 与 Vector 组合算子Matmul 后接 Vector 后处理是融合算子的典型形态Section 边界同时形成 Module 边界Cube ModuleA × B → intermediate ↓ Vector Moduleintermediate → 激活、归约或归一化 → output两个 Module 可以由同一次 Kernel 启动完成。中间数据可以写到 GM workspace也可以走move接口支持的片上通路。R0 记录数据的写入位置、读取位置和同步方向具体同步点和event_id在 R6 确定规则见 cross_core_synchronization.md。这类整数 Cube 收缩 FP 浮点 Vector 尾处理的融合形态在仓库的 cv-quant-matmul-direct-epilogue.md 模式文档中有详细约束整数收缩在 K 全过程中保持 int32再在同一 kernel 内进入 FP 浮点 Vector epilogueepilogue 顺序整数 pre-bias → FP32 scale → 可选 offset → 浮点 post-bias → 最终 cast必须从 golden 冻结。由于 Cube 与 Vector 分属不同 Module跨执行域的数据交接Acc 直搬 Vec或经 GM workspace与 READY/FREE 信用同步必须按跨核同步规则设计。关于 Cube 路径的数据通路api_mapping_and_tile_planning.md 给出了完整的链路pl.matmul(dst, lhs, rhs)要求lhs位于 LeftL0A、rhs位于 RightL0B、dst位于 AccL0C一次矩阵乘通常展开为GM → Mat(L1) → Left/Right(L0A/L0B) → matmul/matmul_acc → Acc(L0C) → store/move → GM 或 Vec(UB)。VF 路径则呈现GM → Vec(UB) → vf.load* → 寄存器 → vf.* → vf.store* → Vec(UB) → GM的层次。这些通路信息正是第二步判断 Section 归属时的硬件依据。R0 输出Module 列表与 module_interfaces.yamlR0 的产出落在两个地方DESIGN.md 的 §0以及机器可读的module_interfaces.yaml契约。Module 列表每个 Module 记录数学目标、Section、输入、输出和前置依赖Module数学目标Section输入输出前置依赖Module 1...Cube/Vector.........只有一个 Module时必须说明数据依赖和 Section 边界为何不需要继续拆分例如纯逐元素、单遍完成且不跨执行域的算子。跨 Tile 状态及其循环范围在 R4 补充——即 loop_design.md 中结果单元与跨 Tile 状态生命周期的职责。DESIGN.md 模板 §0 还要求记录归约轴容量结论归约轴是否可能超单 tile、多 Tile 归约方案常规多遍 / 在线统计加输出两遍 / 其他以及 Module 级数据流示意。这些信息直接呼应 R0 的划分依据。module_interfaces.yaml 契约R0 阶段必须产出机器可读的module_interfaces.yamlsingle source of truth包含以下字段module_countModule 总数is_fusion是否为融合算子同时含 cube 和 vec section →true。按 R0 定义is_fusiontrue隐含module_count 2Cube 和 Vector 必须划分到不同 Modulehas_cross_core是否涉及 cross_core 跨核流水信息记录用不影响分流判据modules[]每个 Module 的id/name/description/sectioncube或vector/golden_steps/inputssource 为primary或module_j且j 当前 id/outputs/golden_stage_fnfinal_outputs每个 golden 返回值对应到产出 Modulecomposition_verificationatol / rtol / seeds / shapes。骨架可由脚本自动生成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脚本 gen_module_interfaces.py 会从{op}_golden_cpu.py中解析 golden 函数签名自动填充schema_version、op、primary_inputs与composition_verification等确定性部分modules[]边界、final_outputs接线、is_fusion、has_cross_core等标注TODO的判断部分由 architect 手工填写。产出后必须自验python ./scripts/validate_module_yaml.py custom/op/module_interfaces.yaml --json返回status: PASS才算完成。验证器 validate_module_yaml.py 内置了完整的契约规则rule0–rule7文件必须是声明了op/module_count/modules的非空映射且至少一个 Module防止生成器错误消息被重定向进 YAML 后空文件通过校验inputs[*].source primary的名称必须存在于primary_inputssource module_j必须满足j 当前 id且名称存在于module_j.outputs禁止前向/自引用final_outputs的module_j必须满足j module_count且名称存在于对应 Module 输出不允许 no-op Moduleshape 表达式只允许 - * //与名称/整数 tokendtype 必须在允许的词汇表内module_count必须等于len(modules)。该脚本还支持--self-test运行全部规则的自检用例其中记录了从旧拼写phase_j到规范拼写module_j的演进两套拼写目前均被接受以保证历史产物继续通过校验。Module 划分与后续轮次的衔接Module 划分不是孤立的设计动作它与后续轮次存在明确的上下游关系R1API 映射在 R0 的 Module 划分基础上将 API 调用链细化到 Module 内部每一步操作R4循环与 SectionR4不重新划分 Module只负责把 R0 的 Module 落实到 Section 与循环结构若 API 数据通路与 R0 记录的 Section 归属冲突需回到 R0 修正 Module 划分R6跨核同步Cube 与 Vector 之间的数据交接使用set_cross_core/wait_cross_coreR0 记录数据的写入位置、读取位置和同步方向R6 落实具体同步点与event_idR8综合评估数据依赖是否正确Module 顺序、sync 位置与跨 tile 状态是否正确初始化和持久化均可能回溯到 R0 重新评估。其中最关键的一条回溯链是归约轴超单 tile 时回 R0。当 R7.5 目标测试 case 验证发现归约轴超单 Tile 的跨 tile 状态与持久化适配不了时要回溯 R0 重新设计归约方案常规多遍 / 在线统计加输出两遍而不是删改测试 case 迁就设计。这再次印证了 R0 Module 划分质量对整条设计链的决定性影响。小结Module 划分的两把尺子是数据依赖决定必须分开Section 边界决定按执行域分开同时片上空间容量可以强制制造额外的拆分边界。判断边界时始终记住是否需要保存中间结果不构成划分依据Scaling不是 Section一个 Module 只能属于一个 Section。对 Softmax 类多遍遍历算子边界来自基于最终结果重新遍历归约轴对 CubeVector 融合算子Section 边界就是 Module 边界跨域交接的同步细节交给 R6。最终把划分结论同时落进 DESIGN.md §0 的 Module 列表与机器可读的module_interfaces.yaml并通过 validate_module_yaml.py 的 PASS 校验R0 才算真正闭环。【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表