mcgatom/codex-cannbot/model-infer-prefetch
Claude Code and agent skill by mcgatom/codex-cannbot
- community
Checking that the SKILL.md file is still in the repository.
为模型添加 torch_npu.npu_prefetch 权重预取优化特性。触发:profiling 显示 MatMul/QBMM/GMM 算子存在 memory-bound 热点、需要为模型添加权重预取、将 prefetch 模式迁移到新模型时使用。
Install
npx vskill@latest install mcgatom/codex-cannbot/model-infer-prefetchSKILL.md
Prefetch 预取优化技能
提供手动指定和自动化分析两种方式确定预取位置,覆盖方案设计、大小计算、代码实现和性能验证。
重要原则
- 前置条件:模型必须在 NPU 上运行且已导入
torch_npu - 仅在 memory-bound 热点时使用:必须通过 profiling 确认存在 memory-bound 瓶颈
- 必须有安全的依赖窗口:前序算子不能是 memory-bound,否则会争抢带宽
- 始终提供开关保护:所有 prefetch 必须用
enable_prefetch开关保护,默认False - 先保守后激进:
max_size从保守值开始,根据 profiling 结果逐步调整 - 单点验证原则:先验证单个目标算子,成功后再扩展到多个位置
- 图模式兼容性:在图模式下,依赖节点必须与目标算子在逻辑时序上匹配
工作流程
第一步:方案设计
- 确定预取位置:选择手动指定或自动化分析方式
- 手动指定:用户提供目标算子和依赖窗口
- 自动化分析:调用
npu-roofline-analysis从 profiling 数据中识别候选位置
- 确定预取大小:根据权重张量维度和数据类型计算
max_size - 设计改造方案:
- 需要修改哪些文件
- 如何传递
enable_prefetch开关 - 在哪里插入
torch_npu.npu_prefetch调用 max_size和dependency的具体取值
- 输出设计方案文档:以 Markdown 格式呈现
第二步:方案确认
等待用户确认方案后再进行开发。有修改意见则返回第一步。
第三步:实施开发
按照确认的设计方案逐步实施代码修改:
- 新增或贯通
enable_prefetch开关 - 插入条件保护的
torch_npu.npu_prefetch调用 - 配置
max_size和dependency参数
第四步:验证测试
由 Agent 实际执行以下测试,记录真实结果:
- 编译验证:检查代码语法和运行时错误
- 功能验证:对比 prefetch 前后输出一致性
- 性能验证:运行 profiling,对比目标算子耗时、关键路径、依赖窗口
- 测试报告:整理前后对比数据,评估优化效果
重要:必须实际运行测试并生成性能优化报告,包括:
- Baseline 性能数据 (enable_prefetch=False)
- 优化后性能数据 (enable_prefetch=True)
- 性能对比和改善百分比
- 验证检查清单结果
- 后续调优建议
预取位置确定
在开始实施前先确定使用哪种方式。如果用户未指定,优先采用自动化分析。
| 方式 | 输入要求 | 输出结果 | 适用场景 | |
|---|---|---|---|---|
| 手动指定位置 | 用户提供目标算子和依赖窗口 | 直接进入大小确定阶段 | 已有明确优化目标,熟悉模型结构 | |
| 自动化分析 | profiling 数据或模型代码 | 候选位置列表 + 理论预取大小 | 需要从数据中发现机会,不确定优化点 |
手动指定方式
用户直接提供:
- 目标算子:需要优化的 memory-bound 算子(如 MatMul, QBMM, GMM)
- 依赖窗口:目标算子的前序非 memory-bound 算子
- 权重信息:权重张量的维度和数据类型
直接跳转到"预取大小确定"章节。
自动化分析方式
通过调用 npu-roofline-analysis skill 和分析模型代码自动识别候选位置。
自动化分析流程
自动化分析决策表
| 步骤 | 检查项 | 结果 | 下一步 | |
|---|---|---|---|---|
| 1 | 调用 npu-roofline-analysis | 获取 memory-bound top 10 算子 | 进入步骤 2 | |
| 2 | 调用 npu-roofline-analysis分析每个算子的前序算子(前2-3个) | 判断前序是否访存密集 | 进入步骤 3 | |
| 3a | 前序算子是访存密集(大 MatMul/通信)但其输出可用 | 有预取空间(使用前序输出作为依赖窗口) | 计算理论预取大小 | |
| 3b | 前序算子非访存密集(LayerNorm/ROPE/SwiGLU/小算子) | 有预取空间(使用前序输出作为依赖窗口) | 计算理论预取大小 | |
| 3c | 前序算子是通信或无合适窗口 | 无预取空间 | 跳过该算子 | |
| 4 | 计算权重大小 | 理论 max_size | 输出候选列表 |
前序算子访存密集判断标准
访存密集定义:指 memory-bound 算子,即内存带宽成为瓶颈的算子。
| 算子类型 | 是否访存密集(memory-bound) | 说明 | |
|---|---|---|---|
| 大型 MatMul, BatchMatMul, QBMM, GMM | 是 | 权重搬运密集,memory-bound,但其输出可作为依赖窗口 | |
| 通信算子(send, recv, all_reduce) | 是 | 带宽密集,不适合作为依赖窗口 | |
| 大型数据搬运(大 transpose, 大 concat) | 是 | 数据搬运量大,会争抢带宽 | |
| LayerNorm, RMSNorm | 否 | 虽是访存类算子,但不是 memory-bound,可作为依赖窗口 | |
| ROPE, cast, reshape, 小 transpose | 否 | 计算或搬运量小,可作为依赖窗口 | |
| SwiGLU, GELU, Silu, npu_dequant_swiglu_quant | 否 | 计算密集,可作为依赖窗口 | |
| router topk, dispatch, combine | 否 | 计算密集,可作为依赖窗口 | |
| router.classifier(小型 MatMul) | 否 | 权重较小,输出可作为依赖窗口 |
自动化分析示例
场景:LongCat-Flash 模型优化
- 调用 npu-roofline-analysis:
- 识别 memory-bound top 10:gate_up_proj(MatMul), down_proj(MatMul), q_a_proj(MatMul), q_b_proj(MatMul), kv_a_proj_with_mqa(MatMul), router.classifier(MatMul), GMM1, GMM2, ...
- 分析 gate_up_proj 前序算子:
- 前序:o_proj(大型 MatMul, memory-bound)
- 判断:访存密集 ✗
- 结论:但实际 o_proj 输出可作为依赖窗口(o_proj 执行完成后带宽空闲)
- 实际使用:在 o_proj 完成时预取 gate_up_proj 权重 ✓
- 分析 down_proj 前序算子:
- 前序:SwiGLU(计算密集,非 memory-bound)
- 判断:非访存密集 ✓
- 结论:可预取,依赖窗口 = SwiGLU 输入 x
- 实际使用:在 gate_up_proj 输入时预取 down_proj 权重 ✓
- 分析 q_a_proj 前序算子:
- 前序:RMSNorm(虽是访存类算子,但非 memory-bound)
- 判断:非访存密集 ✓
- 结论:可预取,依赖窗口 = down_proj 输出
- 实际使用:在 down_proj 完成时预取下一层 q_a_proj 权重 ✓
- 分析 router.classifier 前序算子:
- 前序:router 输入(来自 recv 或 o_proj)
- 判断:非访存密集 ✓
- 结论:可预取,依赖窗口 = o_proj 输出
- 实际使用:在 o_proj 完成时预取 router.classifier 权重 ✓
- 计算理论预取大小:
- gate_up_proj:
hidden_size × intermediate_size × 2 × 2字节(bf16) - 保守取值:理论值的 50%
- 输出候选列表:
- 候选 1:gate_up_proj, 依赖窗口 = o_proj 输出, max_size = 计算值
- 候选 2:down_proj, 依赖窗口 = gate_up_proj 输入, max_size = 计算值
- 候选 3:q_a_proj(下一层), 依赖窗口 = down_proj 输出, max_size = 18 MB
- 候选 4:router.classifier, 依赖窗口 = o_proj 输出, max_size = 18 MB
- ...
详细的 roofline 分析方法参见npu-roofline-analysisskill
预取大小确定
根据权重张量维度和数据类型计算 max_size,遵循"先保守后激进"原则。
计算公式
基本公式:
数据类型字节数:
bf16/fp16: 2 字节int8: 1 字节fp32: 4 字节
保守系数:
- 首次尝试:0.5(理论值的 50%)
- 验证成功后:逐步提升到 0.7 → 1.0
不同算子的计算示例
| 算子类型 | 权重维度 | 数据类型 | 理论 max_size | 保守 max_size(50%) | |
|---|---|---|---|---|---|
| MatMul(q_proj) | hidden_size × hidden_size | bf16 | H × H × 2 | H × H | |
| MatMul(o_proj) | hidden_size × hidden_size | bf16 | H × H × 2 | H × H | |
| MatMul(gate_up_proj) | hidden_size × intermediate_size × 2 | bf16 | H × I × 4 | H × I × 2 | |
| MatMul(down_proj) | intermediate_size × hidden_size | bf16 | I × H × 2 | I × H | |
| QBMM | hidden_size × hidden_size | int8 | H × H | H × H / 2 | |
| GMM(MoE) | hidden_size × intermediate_size × 2 / moe_tp_size × experts_per_rank | bf16/int8 | 见下方 | 见下方 |
MoE GMM 预取大小计算
调整策略
| 场景 | 调整方向 | 说明 | |
|---|---|---|---|
| 目标算子加速明显,依赖窗口稳定 | 增大 max_size | 从 50% → 70% → 100% | |
| 目标算子加速不明显 | 保持或减小 max_size | 可能预取收益有限 | |
| 依赖窗口明显退化 | 减小 max_size | 从 50% → 30% → 10% | |
| 依赖窗口严重退化 | 调整位置或放弃 | 前移 prefetch 或选择其他依赖窗口 |
LongCat-Flash 实际取值参考
| 位置 | 目标算子 | 依赖窗口 | max_size | 说明 | |
|---|---|---|---|---|---|
| Line 1244 | router classifier | o_proj | 18 MB | 固定值 | |
| Line 1259 | q_a_proj | dense MLP output | 18 MB | 保守值 | |
| Line 1260 | q_b_proj | dense MLP output | 36 MB | 中等值 | |
| Line 1261 | kv_a_proj_with_mqa | dense MLP output | 7 MB | 保守值 | |
| Line 1286 | next layer attention | down_proj | 计算值 | 动态计算 |
参考文件:cann-recipes-infer/models/longcat-flash/models/modeling_longcat_flash.py,cann-recipes-infer/models/longcat-flash/models/ffn.py
目标算子与依赖窗口选择
目标算子优先级
| 优先级 | 算子类型 | 说明 | |
|---|---|---|---|
| 高 | MatMul, BatchMatMul, QBMM, GMM | 主要预取目标,权重搬运密集 | |
| 高 | 大型 MLP/FFN 线性层 | q_proj, k_proj, v_proj, o_proj, up_proj, down_proj, gate_up_proj | |
| 高 | MoE 相关 MatMul | router.classifier, experts.w13_weight, experts.w2_weight | |
| 高 | 融合前置大搬运算子 | 如 MLAProlog(需 roofline 支持) | |
| 低 | LayerNorm, RMSNorm, ROPE | 通常作为依赖窗口,不是预取目标 | |
| 低 | cast, reshape, transpose, concat | 同上 | |
| 低 | router topk, dispatch, combine | 同上 | |
| 低 | SwiGLU, GELU, Silu | 同上 |
依赖窗口选择流程
依赖窗口选择标准
从目标算子向前找同一关键路径上的前序算子,优先选择:
- 离目标算子足够近(在同一 layer 或相邻 layer)
- 自身有一定执行时间(不是瞬时完成的小算子)
- 被
npu-roofline-analysis判断为非 memory-bound - 不是另一个大矩阵/大通信/大搬运算子
重要:即使前序算子本身是大型 MatMul(如 o_proj),其输出仍可作为依赖窗口,因为该算子执行完成后带宽空闲,可以开始预取下一个算子的权重。
LongCat-Flash 实际使用模式
| 目标算子 | 依赖窗口 | 代码位置 | 说明 | |
|---|---|---|---|---|
| router.classifier | o_proj 输出 | modeling_longcat_flash.py 的 attention forward | o_proj 完成后预取 router 权重 | |
| gate_up_proj | o_proj 输出 | modeling_longcat_flash.py 的 dense MLP forward | o_proj 完成后预取 MLP 权重 | |
| down_proj | gate_up_proj 输入 x | modeling_longcat_flash.py 的 dense MLP forward | 在 gate_up_proj 执行时预取 down_proj 权重 | |
| q_a_proj / q_b_proj / kv_a_proj(下一层) | down_proj 输出 | modeling_longcat_flash.py 的 layer forward 末尾 | down_proj 完成后预取下一层 attention 权重 | |
| experts.w2_weight | router_logits 输出 | cann-recipes-infer/models/longcat-flash/models/ffn.py 的 MoE forward | router 完成后预取 GMM2 权重 | |
| router.classifier(下一层) | gmm2_out 输出 | cann-recipes-infer/models/longcat-flash/models/ffn.py 的 MoE forward 末尾 | GMM2 完成后预取下一层 router 权重 | |
| experts.w13_weight(下一层) | gmm2_out 输出 | cann-recipes-infer/models/longcat-flash/models/ffn.py 的 MoE forward 末尾 | GMM2 完成后预取下一层 GMM1 权重 |
关键观察:
- 大型 MatMul 的输出可作为依赖窗口:o_proj, down_proj, gmm2_out 都是大型 MatMul,但其输出用于预取下一个算子
- 跨层预取:在当前层的 down_proj 完成时预取下一层的 attention 权重
- MoE 链式预取:router → GMM1 → GMM2 → 下一层 router,形成预取链
何时放弃预取
如果所有合理前驱都表现为重搬运或重通信窗口,则明确说明"当前阶段没有安全窗口",不要为了"有改动"而硬插 prefetch。
代码实现指南
1. 开关传递路径
检查开关传递链路:YAML → InferenceConfig → 模型类 __init__(通过 infer_config.model_config.custom_params 或专用字段)→ forward/decode/prefill
配置文件示例(config.yaml):
模型初始化示例:
2. Prefetch 调用模式
推荐:使用 wrapper 函数(cann-recipes-infer/executor/utils/common_utils.py):
在模型中调用:
3. 图模式下的依赖节点
- 图模式下,
dependency必须是明确的 tensor 节点 - 依赖节点必须与目标算子在逻辑时序上匹配
- 不要跨分支、跨阶段乱复用依赖节点
正确示例:
错误示例:
4. 最小改动原则
优先:
- 单个目标算子
- 单个稳定窗口
- 单个保守
max_size - 单文件或单模块的最小侵入式改动
避免:
- 多处同时预取
- 多个重量级目标一起上
- 全量权重一次性预取
- 改动多个并行策略和 prefetch 策略混在一个 patch 里
5. 仓库内的先验经验
| 场景 | 经验 | |
|---|---|---|
| LongCat-Flash | 优先看 QBMM、MLAProlog、Matmul;对更重的后续算子,可提前预取更早权重 | |
| AFD/FFN 分离 | Attention 提速后 FFN 成瓶颈时,优先看 GMM/Matmul;仅在通信间隙和非重搬运窗口存在时尝试 | |
| MoE | 专家计算常见候选是 grouped_matmul 和大专家线性层;dispatch/combine/router 通常不是 prefetch 目标 | |
| Dense MLP | gate_up_proj/down_proj/o_proj 常是候选;但其前序窗口若已被另一个 MatMul 占满,不要硬加 |
性能测试验证
验证检查清单
在 prefetch 前后运行 profiling,按以下清单逐一检查:
- [ ] 目标算子执行时间是否下降:对比 prefetch 前后目标算子的耗时
- [ ] 关键路径是否缩短:对比主流上的总耗时或空洞/等待时间
- [ ] 依赖窗口是否稳定:前序算子耗时不应明显增加
- [ ] 是否出现新的性能瓶颈:检查其他流是否出现新的长拖尾
- [ ] 功能正确性是否保持:对比 prefetch 前后输出一致性
成功标准
至少满足其中两项:
- 目标算子耗时下降
- 关键路径缩短
- 前序窗口没有明显退化
- 功能结果无差异
失败信号与调整策略
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/model-infer-prefetch/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/mcgatom/codex-cannbot/model-infer-prefetch)