CANN Runtime API 模块拆分:四 PR 分批上库流程实战指南
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
CANN Runtime 仓库在按官方对外接口文档将巨型主Api/ApiImpl/decorator 链路拆分为独立ApiXxx/ApiImplXxxAPI 大类时,采用"独立文档 PR -> 框架 PR -> 路由 PR -> 清理 PR"的四 PR 分批上库模式,保证每个阶段可独立编译、验证和回退。本文完整讲解这套上库流程的每条前置原则、分支与提交规范、阶段检查命令、PR 描述模板、CI 门禁口径以及三阶段代码 PR 各自的审查重点,并结合仓库中 Event 拆分的真实批次(event-batches.md)说明落地方式。读完本文,你将能够按仓库规范为 Runtime API 模块拆分规划、提交并验收一整套分批上库 PR。
一、这套流程解决什么问题
Runtime 长期演进后,大量对外接口的 C API 入口集中在 src/runtime/api/api_c_*.cc 系列文件中,实际实现则堆积在单一主Api、ApiImpl及其 decorator(api_decorator.cc)链路上。拆分工作的技术方法见 method.md,其核心链路是:
官方 acl/aclrt 接口 -> ACL 实现与 rt/rts 调用 -> src/runtime/api/api_c*.cc C 入口 -> 当前 Api 大类及成员函数 -> ApiImpl/平台实现/对象实现 -> CMake、stub、UT把"一个官方模块"拆成新ApiXxx大类,如果一次性把所有改动压进一个 PR,会产生巨型 diff:既有新框架代码、C API 路由切换,又有旧链路删除,任何一步出问题都难以定位和回退。因此仓库规定:业务上永远是"框架 -> 路由 -> 清理"三个串行阶段,首次交付则固定为 4 个 PR(文档 PR + 三个代码阶段 PR),每一个 PR 只包含一个阶段、一个清晰提交。
以 Event 拆分为例(见 event-batches.md),首批 IPC Event 的落地就是:框架 PR(新增 api_event.hpp、api_impl_event.cc 等)-> 框架稳定化 -> 路由 PR(aclrtIpcGetEventHandle/aclrtIpcOpenEventHandle切到ApiEvent)-> 清理 PR(删除主Api旧链路)。三个代码阶段严格串行,后一个 PR 必须基于前一个实际合入后的最新主线。
二、前置原则:一次只做一件事
上库流程的第一步是确认以下 6 条前置原则,它们决定了所有后续操作的口径:
- 首次交付固定为 4 个 PR:独立文档 PR -> 第一批框架 PR -> 第一批路由 PR -> 第一批清理 PR。
- 三个代码业务阶段串行合入:框架 -> 路由 -> 清理,不允许并行推进或颠倒顺序。
- 文档 PR 纯净:只包含分析和方案交付件,不混入源代码、CMake 或 UT 改动。
- 基于实际主线:每个阶段基于前一阶段实际合入后的最新
origin/master,不要长期堆叠在未合入提交上。 - 单 PR 单阶段:每个 PR 只包含一个阶段和一个清晰提交;检视清理确有必要时使用窄 PR,不混入下一阶段。
- 权限边界:推送、创建/更新 PR、发布评论或审批都需要用户明确授权,Agent 不得擅自执行线上写操作。
此外,submission.md特别强调要保留用户工作区中的无关改动:大改优先使用隔离 worktree,避免reset/checkout等可能覆盖用户工作的操作。
三、分支与提交建议
每个阶段使用独立分支,命名规则为:
docs/api-<module>-split-plan refactor/api-<module>-framework refactor/api-<module>-route refactor/api-<module>-cleanup建议的提交标题如下:
docs: 新增 <Module> API 拆分分析与计划 refactor: 新增 Api<Module> 框架 refactor: 切换 <Module> API 调用链 refactor: 清理主 Api <Module> 旧链路若阶段一合入后检视提出确定的依赖收敛意见,需要以窄补丁修复时,可使用:
refactor: 清理 Api<Module> 冗余依赖Event 拆分中实际采用的分支与提交与此一致(event-batches.md),例如框架稳定化 PR 只收敛头文件和具体实现依赖,不改变业务路由,因而提交标题明确为"清理冗余依赖",不属于新的业务阶段。
四、准备阶段:隔离 worktree 与精准提取改动
创建分支前,先确认工作区和远程状态:
git status --short git remote -v git fetch origin master为避免污染用户工作区,建议在隔离目录中基于主线创建分支:
git worktree add <isolated-path> -b refactor/api-<module>-framework origin/mastersubmission.md明确规定不要使用会覆盖用户改动的 reset/checkout 操作。若此前有聚合分支(例如包含完整三阶段验证的临时分支),需要按文件和 hunk 精准提取本阶段内容,而不是整体复制:
git diff --stat origin/master...<aggregate-branch> git diff origin/master...<aggregate-branch> -- <path> git add -p <path> git diff --cached --stat git diff --cached提交前必须确认:暂存内容没有混入下一阶段接口,也没有包含用户无关文件。这一步是"单 PR 单阶段"原则在 git 操作层面的落地,git add -p允许逐 hunk 选择,是避免跨阶段污染的主要手段。
五、阶段提交检查
每个阶段提交后,至少执行以下三项检查:
git diff --check origin/master...HEAD git diff --stat origin/master...HEAD git log --oneline origin/master..HEADgit diff --check:检查空白错误等低级问题;git diff --stat:核对变更文件清单是否落在本阶段范围内;git log --oneline:确认提交历史只有一个本阶段提交。
随后按 risk-and-validation.md 执行对应验证。该文档给出了框架、路由、清理三个阶段的最小验证矩阵,例如框架阶段要证明"现有 C API 仍走旧路径",路由阶段要证明"新路径与旧路径行为等价且旧主Api方法尚未删除",清理阶段要扫描全部旧符号残留并完成正式构建与多平台 UT。
submission.md特别强调:验证失败不能只写"环境问题"。要定位:
- 失败发生在改动前还是改动后;
- 是否进入目标源文件;
- 属于功能回归还是证据缺口。
每个阶段的验证证据(命令、目标、通过/失败数、失败原因)都要真实记录,不得以"计划通过"之类的表述代替实际结果。
六、推送与创建 PR
推送与创建 PR 只能在用户明确要求后进行,完整流程为:
- 读取仓库
gitcode-prSkill(涉及 GitCode PR 时)。 - 读取当前
.gitcode/PULL_REQUEST_TEMPLATE.zh-CN.md,不要使用旧硬编码模板。 - 推送当前阶段分支到用户指定 fork。
- 创建目标为
cann/runtime:master或用户明确指定分支的 PR。 - 若代码发生变更后刷新已有 PR,同步更新 PR 描述中的变更范围、风险、实际验证结果和剩余工作,不得保留与最新代码不一致的旧描述。
- 回读线上 PR 的 title、head/base、提交数、描述和最新 commit,确认与本地一致。
关键的时序约束是:前一阶段 squash/no-merge 合入后,下一阶段要基于实际主线提交刷新,并重新执行完整阶段验证。submission.md提醒不要假设 PR head SHA 等于最终主线 SHA——合入过程可能经历 squash 或 rebase,只有合入后的真实主线提交才可作为下一阶段基线。
七、CI 门禁:每个 PR 的可合入前提
每个 PR 创建或刷新后都必须检查线上 CI,文档 PR 与代码 PR 的门禁不同:
- 文档 PR:适用的文档、格式、OAT 和仓库必选任务必须通过。
- 框架、路由、清理 PR:仓库必选任务和 CI 编译任务必须通过,并结合阶段风险执行对应 UT。
- CI 失败时先定位并修复,推送修复后同步更新 PR 描述,再等待新一轮 CI。
- CI 未触发、仍在运行、编译目标被跳过或产物不可读时,记录为证据缺口,不得写成"CI 编译通过"。
- 只有线上 CI 显示成功且目标编译任务确实执行,才能将代码 PR 标记为可合入。
三个代码 PR 严格串行:前一代码 PR 实际合入且 CI 编译通过后,下一代码 PR 才基于最新主线刷新、验证和提交。这与 risk-and-validation.md 的验收结论分类一致——必须明确区分"已本地验证""已由线上流水线验证""因环境或权限未验证"等状态,CI 产物不可读时写"证据缺口"而非"全部通过"。
八、PR 描述模板与填写要点
PR 描述按仓库模板填充,描述部分至少包含以下内容:
## 描述 本 PR 是 `<模块>` API 大类拆分的 `<框架/路由/清理>` 阶段。 本批接口: - `<aclrtXxx>` -> `<rtXxx>` -> `<ApiXxx::Xxx>` 具体变更: - ... 行为等价说明: - C API 签名、参数校验、返回码、ErrMsg、profiling、Context/线程环境和产品支持保持不变。 本 PR 不包含: - 不切换/不删除下一阶段链路。 - 不整改 `<剩余接口>`;原因是 `<跨模块依赖/后续批次>`。 剩余工作: - ...填写要点(submission.md与 method.md 的一致要求):
- "本批接口"逐条给出 C API -> Runtime 入口 -> 新
ApiXxx::Xxx的映射,这正是 method.md 端到端追踪的成果形态; - "行为等价说明"覆盖行为等价基线的关键维度:C API 签名、参数校验顺序、返回码转换、ErrMsg、profiling begin/end、Context/线程环境以及产品支持矩阵(标准、David/V201、tiny/arch5162、910B 等);
- "本 PR 不包含"明确划清本阶段边界,防止检视者误以为路由阶段会顺带清理旧链路;
- "剩余工作"列出后续批次和暂缓原因。
"如何测试"一节必须写实际执行结果,包括命令、目标、通过数和未完成项;不要预填计划结果。Checklist 只勾选真正完成的项目。这一口径贯穿整套流程:任何"计划验证""预计通过"性质的表述都不被接受。
九、三阶段代码 PR 的审查重点
三个代码阶段虽然共用同一套 PR 规范,但各自的描述重点和验收证据不同。
框架 PR
框架阶段为第一批接口新增或扩展ApiXxx/ApiImplXxx、Runtime 生命周期、构建接入和直接 UT,不切换任何 C API 路由。PR 描述必须强调:
- 现有 C API 未切路由,对外行为没有变化;
- 列出 Runtime 生命周期改动、各产品 CMake/stub 源文件清单和直接 UT;
- 说明新增成员布局(Runtime 新成员位置、虚函数变化)与不支持产品语义。
以 Event 拆分批次一为例(event-batches.md),框架阶段新增 api_event.hpp 与 api_impl_event.cc,标准产品编译正式实现,tiny/arch5162 继续编译 api_impl_event_stub.cc 中的 not-support 桩;创建器(api_impl_creator.cc)按产品返回正确的动态类型。框架阶段合入后如检视提出高置信度的依赖收敛意见,可用窄范围稳定化 PR 处理(如 Event 的 PR 4219),它仍属于框架稳定化,不改变三阶段业务主线。
路由 PR
路由阶段只切换本批选中 C API 的入口到ApiXxx,保留主Api旧方法。PR 描述必须:
- 列出切换的确切 C API 清单(例如
aclrtIpcGetEventHandle、aclrtIpcOpenEventHandle); - 声明旧主
Api方法仍保留、未删除; - 给出路由证明(通过隔离主
Api实例证明 C API 确实进入ApiEvent); - 提供新旧行为对比(成功、空入参、非法 handle、Context/Device 异常、底层失败、feature-not-support、profiling、线程环境)和环境/profiling 验证结果。
路由阶段的风险点在于"表面上切换、实际仍走残留路径",因此 risk-and-validation.md 要求路由 UT 能隔离旧实例并证明参数、结果和输出写回的一致性。
清理 PR
清理阶段在前两阶段稳定后删除主Api、ApiImpl、decorator、平台 stub 和旧 UT 中的本批残留。PR 描述必须:
- 列出删除的 ApiImpl/decorator/platform/UT 范围;
- 声明只清理本批接口,不扩展范围;
- 附残留扫描(对主
Api、ApiImpl、decorator、平台 override/stub、mock 和旧 UT 按确切符号执行rg扫描)、正式构建和多平台 UT 结果。
需要特别注意的是:删除虚函数和 include 后,各产品(标准、910B、tiny、arch5162、cmodel 等)必须均可编译和链接,新虚实现文件一旦漏进某平台源列表就会出现 vtable undefined 符号。因此 event-batches.md 强调:新增虚函数定义必须进入标准、David/V201、cmodel、tiny、arch5162、910B 及对应 UT 源列表,只验证 common 目标不能证明其他平台无链接缺口。
十、合入后收尾
每个 PR 合入后都要执行收尾动作,submission.md给出的清单如下:
- 记录实际主线提交和合入时间。
- 更新后续阶段基线与 PR 描述。
- 重新核对官方模块接口清单和剩余接口。
- 将已完成项标为"本批完成",保留后续/暂缓原因。
- 最后一阶段合入后,确认在线 PR 描述、设计文档和主线代码一致。
其中最后一条是关键的一致性收口:只有在线 PR 描述、设计文档与主线代码三方一致,才算一个模块批次的完整闭环。
最后,submission.md给出了一个重要的结论口径:不得因为三个阶段完成就自动宣称整个官方模块已完成。只有接口全集均有明确归属和处理结论(本批完成、后续整改、暂缓整改、无需整改)时,才能给出模块级完成结论。Event 拆分的状态汇报方式(event-batches.md)就是示例:
已合入:批次一 IPC Event 进行中:批次二查询、时间和标识 下一优先批次:批次三 GetAvailEventNum、EventWorkModeSet/Get(API 模块边界独立) 依赖解耦后决策:批次四 Event 同步(Soma 成功后副作用) 有条件推进:批次五生命周期 暂缓:批次六 Record/Reset 明确保留:StreamWaitEvent、SetOpWaitTimeOut十一、配套资料与仓库依据
- 拆分方法总纲:method.md(端到端映射、批次划分、行为等价基线、三阶段边界)
- 上库与验证标准:risk-and-validation.md(风险矩阵、分阶段最小验证、命令选择、验收结论口径)
- Event 真实批次与后续计划:event-batches.md(22 个 ACL 接口、六个业务批次、三阶段 PR 编号)
- 模块拆分 Skill 主文档:SKILL.md(首次输出四 PR、执行流程、7 项文档交付件)
- 代码结构参考:src/runtime/api/api_c_event.cc(C 入口)、api_event.hpp 与 impl/api_impl_event.cc、impl/api_impl_event_common.cc、impl/api_impl_event_stub.cc(实现层次)、impl/api_decorator.cc(decorator 链)、impl/api_impl_creator.cc(创建器)
- 官方接口文档对照:docs/zh/api_ref/07_event_management.md(用于交叉核对接口清单)
- UT 与构建入口:tests/ut/runtime、tests/build_ut.sh(
--ut=runtime --target=runtime_utest_api等目标)
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考