Provides two methods, manual specification and automated analysis, to determine prefetch locations, covering solution design, size calculation, code implementation, and performance verification.
Important Principles
- Prerequisites:The model must run on the NPU and must have imported
torch_npu - Use only for memory-bound hotspots:Profiling must confirm the existence of a memory-bound bottleneck
- Must have a safe dependency window:The preceding operator must not be memory-bound, otherwise it will contend for bandwidth
- Always provide switch protection:All prefetch must be protected by the
enable_prefetchswitch, defaultFalse - Conservative first, then aggressive:
max_sizeStart from a conservative value and gradually adjust based on profiling results - Single-point verification principle:First verify a single target operator, and only after success expand to multiple locations
- Graph mode compatibility:In graph mode, the dependency node must match the target operator in logical timing
Workflow
Step 1: Solution Design
- Determine prefetch locations:Choose manual specification or automated analysis
- Manual specification: the user provides the target operator and dependency window
- Automated analysis: call
npu-roofline-analysisto identify candidate locations from profiling data
- Determine prefetch size:Calculate
max_size - Design the modification plan:
- Which files need to be modified
- How to pass the
enable_prefetchswitch - Where to insert the
torch_npu.npu_prefetchcall max_sizeanddependencyspecific values
- Output the design document:Present it in Markdown format
Step 2: Solution Confirmation
Wait for the user to confirm the solution before proceeding with development. If there are modification suggestions, return to Step 1.
Step 3: Implementation and Development
Implement the code modifications step by step according to the confirmed design solution:
- Add or connect through the
enable_prefetchswitch - Insert the conditionally protected
torch_npu.npu_prefetchcall - Configure the
max_sizeanddependencyparameters
Step 4: Verification Testing
The Agent actually executes the following tests and records the real results:
- Compilation verification:Check code syntax and runtime errors
- Functional verification:Compare output consistency before and after prefetch
- Performance verification:Run profiling and compare the target operator latency, critical path, and dependency window
- Test report:Organize before-and-after comparison data and evaluate the optimization effect
Important:Tests must actually be run and a performance optimization report must be generated, including:
- Baseline performance data (enable_prefetch=False)
- Optimized performance data (enable_prefetch=True)
- Performance comparison and improvement percentage
- Verify Checklist Results
- Follow-up Tuning Suggestions
Prefetch Position Determination
Determine which method to use before starting implementation. If the user does not specify, prefer automated analysis.
| Method | Input Requirements | Output Result | Applicable Scenarios |
|---|---|---|---|
| Manually Specify Position | User provides target operator and dependency window | Proceed directly to the size determination stage | Already have a clear optimization target and are familiar with the model structure |
| Automated Analysis | profiling data or model code | Candidate position list + theoretical prefetch size | Need to discover opportunities from data and are unsure of the optimization points |
Manual Specification Method
The user directly provides:
- Target Operator: the memory-bound operator to optimize (such as MatMul, QBMM, GMM)
- Dependency Window: the preceding non-memory-bound operator of the target operator
- Weight Information: the dimensions and data type of the weight tensor
Jump directly to the "Prefetch Size Determination" section.
Automated Analysis Method
By calling the npu-roofline-analysis skill and analyzing the model code to automatically identify candidate positions.
Automated Analysis Process
开始
│
├─→ 步骤 1:调用 npu-roofline-analysis
│ ↓
│ 获取 memory-bound top 10 算子列表
│ ↓
├─→ 步骤 2:对每个 memory-bound 算子
│ ├── 分析前序算子
│ ├── 判断前序是否访存密集
│ │
│ ├─→ 前序是访存密集 ──→ 无预取空间 ──→ 跳过该算子
│ │
│ └─→ 前序非访存密集 ──→ 有预取空间 ──→ 标记为候选
│ ↓
├─→ 步骤 3:计算理论预取大小
│ ├── 获取权重张量维度
│ ├── 获取数据类型(bf16/int8)
│ └── 计算 max_size = 维度 × 字节数
│ ↓
└─→ 输出:候选位置列表 + 理论预取大小
Automated Analysis Decision Table
| Step | Check Item | Result | Next Step |
|---|---|---|---|
| 1 | Call npu-roofline-analysis |
Get the top 10 memory-bound operators | Proceed to step 2 |
| 2 | Call npu-roofline-analysisAnalyze each operator's preceding operators (the previous 2-3) |
Determine whether the preceding operator is memory-intensive | Proceed to step 3 |
| 3a | The preceding operator is memory-intensive (large MatMul/communication) but its output is usable | There is prefetch space (use the preceding output as the dependency window) | Calculate the theoretical prefetch size |
| 3b | The preceding operator is not memory-intensive (LayerNorm/ROPE/SwiGLU/small operators) | There is prefetch space (use the preceding output as the dependency window) | Calculate the theoretical prefetch size |
| 3c | The preceding operator is communication or there is no suitable window | No prefetch space | Skip this operator |
| 4 | Calculate the weight size | Theoretical max_size | Output the candidate list |
Criteria for Determining Whether the Preceding Operator Is Memory-Intensive
Definition of Memory-Intensive: refers to memory-bound operators, that is, operators where memory bandwidth becomes the bottleneck.
| Operator Type | Whether Memory-Intensive (memory-bound) | Description |
|---|---|---|
| Large MatMul, BatchMatMul, QBMM, GMM | Yes | Weight movement is intensive, memory-bound, but itsoutput can be used as a dependency window |
| Communication operators (send, recv, all_reduce) | Yes | Bandwidth-intensive, not suitable as a dependency window |
| Large data movement (large transpose, large concat) | Yes | Large data movement volume, will compete for bandwidth |
| LayerNorm, RMSNorm | No | Although it is a memory access operator, it is not memory-bound,can serve as a dependency window |
| ROPE, cast, reshape, small transpose | No | Computation or transfer volume is small,can serve as a dependency window |
| SwiGLU, GELU, Silu, npu_dequant_swiglu_quant | No | Compute-intensive,can serve as a dependency window |
| router topk, dispatch, combine | No | Compute-intensive,can serve as a dependency window |
| router.classifier(small MatMul) | No | Weights are relatively small,the output can serve as a dependency window |
Automated analysis example
Scenario:LongCat-Flash model optimization
-
Call npu-roofline-analysis:
- Identify 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, ...
-
Analyze the preceding operators of gate_up_proj:
- Preceding:o_proj(large MatMul, memory-bound)
- Judgment:memory access intensive ✗
- Conclusion:but in practice the o_proj output can serve as a dependency window (bandwidth is idle after o_proj finishes execution)
- Actual use:prefetch gate_up_proj weights when o_proj completes ✓
-
Analyze the preceding operators of down_proj:
- Preceding:SwiGLU(compute-intensive, not memory-bound)
- Judgment:not memory access intensive ✓
- Conclusion:can prefetch, dependency window = SwiGLU input x
- Actual use:prefetch down_proj weights when gate_up_proj is input ✓
-
Analyze the preceding operators of q_a_proj:
- Preceding:RMSNorm(although it is a memory access operator, it is not memory-bound)
- Judgment:not memory access intensive ✓
- Conclusion:can prefetch, dependency window = down_proj output
- Actual use:prefetch the next layer's q_a_proj weights when down_proj completes ✓
-
Analyze the preceding operators of router.classifier:
- Preceding:router input(from recv or o_proj)
- Judgment:not memory access intensive ✓
- Conclusion:can prefetch, dependency window = o_proj output
- Actual use:prefetch router.classifier weights when o_proj completes ✓
-
Calculate the theoretical prefetch size:
- gate_up_proj:
hidden_size × intermediate_size × 2 × 2bytes (bf16) - Conservative value:50% of the theoretical value
- gate_up_proj:
-
Output the candidate list:
- Candidate 1:gate_up_proj, dependency window = o_proj output, max_size = calculated value
- Candidate 2:down_proj, dependency window = gate_up_proj input, max_size = calculated value
- Candidate 3:q_a_proj(next layer), dependency window = down_proj output, max_size = 18 MB
- Candidate 4:router.classifier, dependency window = o_proj output, max_size = 18 MB
- ...
For the detailed roofline analysis method, see
npu-roofline-analysisskill
Determining the prefetch size
Calculate based on the weight tensor dimensions and data type max_size, following the principle of "be conservative first, then aggressive".
Calculation formula
Basic formula:
max_size = 权重维度乘积 × 数据类型字节数 × 保守系数
Data type byte count:
bf16/fp16: 2 bytesint8: 1 bytefp32: 4 bytes
Conservative coefficient:
- First attempt: 0.5 (50% of theoretical value)
- After successful verification: gradually increase to 0.7 → 1.0
Calculation examples for different operators
| Operator type | Weight dimension | Data type | Theoretical max_size | Conservative 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 | See below | See below |
MoE GMM prefetch size calculation
# 数据类型字节数
dtype_bit = 1 if quant_mode == "w8a8" else 2 # int8: 1 字节, bf16: 2 字节
# GMM1(gate_up_proj)预取大小
gmm1_prefetch_size = hidden_size * intermediate_size * 2 * dtype_bit // moe_tp_size * experts_per_rank // 2
# GMM2(down_proj)预取大小
gmm2_prefetch_size = hidden_size * intermediate_size * dtype_bit // moe_tp_size * experts_per_rank
Adjustment strategy
| Scenario | Adjustment direction | Description |
|---|---|---|
| Target operator acceleration is significant, dependency window is stable | Increase max_size | From 50% → 70% → 100% |
| Target operator acceleration is not significant | Keep or decrease max_size | Prefetch benefit may be limited |
| Dependency window degrades significantly | Decrease max_size | From 50% → 30% → 10% |
| Dependency window degrades severely | Adjust position or abandon | Move prefetch earlier or choose a different dependency window |
LongCat-Flash actual value reference
| Position | Target operator | Dependency window | max_size | Description |
|---|---|---|---|---|
| Line 1244 | router classifier | o_proj | 18 MB | Fixed value |
| Line 1259 | q_a_proj | dense MLP output | 18 MB | Conservative value |
| Line 1260 | q_b_proj | dense MLP output | 36 MB | Moderate value |
| Line 1261 | kv_a_proj_with_mqa | dense MLP output | 7 MB | Conservative value |
| Line 1286 | next layer attention | down_proj | Calculated value | Dynamically calculated |
Reference files:
models/longcat-flash/models/modeling_longcat_flash.py,models/longcat-flash/models/ffn.py
Target operator and dependency window selection
Target operator priority
| Priority | Operator type | Description |
|---|---|---|
| High | MatMul, BatchMatMul, QBMM, GMM | Main prefetch target, weight transfer intensive |
| High | Large MLP/FFN linear layer | q_proj, k_proj, v_proj, o_proj, up_proj, down_proj, gate_up_proj |
| High | MoE-related MatMul | router.classifier, experts.w13_weight, experts.w2_weight |
| High | Fused preceding large transfer operator | Such as MLAProlog (requires roofline support) |
| Low | LayerNorm, RMSNorm, ROPE | Usually serves as a dependency window, not a prefetch target |
| Low | cast, reshape, transpose, concat | Same as above |
| Low | router topk, dispatch, combine | Same as above |
| Low | SwiGLU, GELU, Silu | Same as above |
Dependency window selection process
开始选择依赖窗口
│
├─→ 找到目标算子的紧邻前驱
│ ↓
│ 检查:是否 memory-bound?
│ ├─→ 是 ──→ 前移到更早的前驱 ──→ 重新检查
│ │
│ └─→ 否 ──→ 检查:是否有足够执行时间?
│ ├─→ 是 ──→ 选择为依赖窗口 ✓
│ │
│ └─→ 否 ──→ 前移到更早的前驱 ──→ 重新检查
│
└─→ 所有前驱都不合适 ──→ 报告"无安全窗口" ──→ 放弃该目标
Dependency window selection criteria
From the target operator, search backward for preceding operators on the same critical path, prioritizing:
- Close enough to the target operator (in the same layer or adjacent layer)
- Has some execution time of its own (not an instantaneous small operator)
- Judged by
npu-roofline-analysisasnon memory-bound - Is not another large matrix/large communication/large transfer operator
Important: Even if the preceding operator itself is a large MatMul (such as o_proj), itsoutputcan still serve as a dependency window, because after that operator finishes executing, bandwidth is idle and prefetching of the next operator's weights can begin.
LongCat-Flash actual usage pattern
| Target operator | Dependency window | Code location | Description |
|---|---|---|---|
| router.classifier | o_proj output | modeling_longcat_flash.py:1244 | Prefetch router weights after o_proj completes |
| gate_up_proj | o_proj output | modeling_longcat_flash.py:210 | Prefetch MLP weights after o_proj completes |
| down_proj | gate_up_proj input x | modeling_longcat_flash.py:239 | Prefetch down_proj weights while gate_up_proj executes |
| q_a_proj (next layer) | down_proj output | modeling_longcat_flash.py:1259 | Prefetch next layer attention weights after down_proj completes |
| q_b_proj (next layer) | down_proj output | modeling_longcat_flash.py:1261 | Same as above |
| kv_a_proj (next layer) | down_proj output | modeling_longcat_flash.py:1263 | Same as above |
| experts.w2_weight | router_logits output | ffn.py:76 | Prefetch GMM2 weights after router completes |
| router.classifier (next layer) | gmm2_out output | ffn.py:137 | Prefetch next layer router weights after GMM2 completes |
| experts.w13_weight (next layer) | gmm2_out output | ffn.py:139 | Prefetch next layer GMM1 weights after GMM2 completes |
Key Observations:
- Outputs of large MatMuls can serve as dependency windows: o_proj, down_proj, and gmm2_out are all large MatMuls, but their outputs are used to prefetch the next operator
- Cross-layer prefetching: prefetch the next layer's attention weights when the current layer's down_proj completes
- MoE chain prefetching: router → GMM1 → GMM2 → next layer router, forming a prefetch chain
When to abandon prefetching
If all reasonable predecessors exhibit heavy-transfer or heavy-communication windows, explicitly state that "there is no safe window at the current stage"; do not force-insert prefetch just to "have a change".
Code Implementation Guide
1. Switch propagation path
Check the switch propagation chain: command line/config file → ModelRunner → model class __init__ → forward/decode/prefill
Config file example(config.yaml):
model_config:
enable_prefetch: true # 默认 false
enable_multi_stream: 2 # prefetch 需要多流支持
Model initialization example:
class YourModel:
def __init__(self, config):
self.enable_prefetch = config.get("enable_prefetch", False)
# prefetch 需要图模式 + 多流
assert not self.enable_prefetch or config.get("enable_multi_stream", 0) > 0
2. Prefetch call pattern
Recommended: use a wrapper function(executor/utils/common_utils.py):
def npu_prefetch(switch_flag, weight, depend, size, offset=0):
if switch_flag:
return torch_npu.npu_prefetch(weight, depend, size, offset)
else:
return None
Call in the model:
from executor.utils.common_utils import npu_prefetch
class YourModel:
def forward(self, x):
# 计算依赖窗口
dependency_output = self.some_layer(x)
# 预取下一个算子的权重
npu_prefetch(
self.enable_prefetch,
weight=self.next_layer.weight,
depend=dependency_output,
size=self.prefetch_size,
offset=0
)
# 执行目标算子
output = self.next_layer(dependency_output)
return output
3. Dependency nodes in graph mode
- In graph mode,
dependencymust be an explicit tensor node - Dependency nodes must match the target operator in logical timing
- Do not arbitrarily reuse dependency nodes across branches or stages
Correct example:
# 依赖节点是目标算子的直接前驱
attn_output = self.attention(x) # 依赖窗口
npu_prefetch(self.enable_prefetch, self.mlp.weight, attn_output, size)
mlp_output = self.mlp(attn_output) # 目标算子
Incorrect example:
# 依赖节点与目标算子不在同一路径
attn_output = self.attention(x)
npu_prefetch(self.enable_prefetch, self.mlp.weight, x, size) # 错误:x 不是 mlp 的直接前驱
mlp_output = self.mlp(attn_output)
4. Principle of minimal changes
Prefer:
- a single target operator
- a single stable window
- a single conservative
max_size - minimally invasive changes to a single file or module
Avoid:
- prefetching in multiple places simultaneously
- deploying multiple heavyweight targets together
- one-time prefetching of all weights
- mixing changes to multiple parallel strategies and prefetch strategies in one patch
5. Prior experience in the repository
| Scenario | Experience |
|---|---|
| LongCat-Flash | Prioritize QBMM, MLAProlog, and Matmul; for heavier subsequent operators, earlier weights can be prefetched in advance |
| AFD/FFN separation | When FFN becomes the bottleneck after Attention speedup, prioritize GMM/Matmul; only attempt when communication gaps and non-heavy-transfer windows exist |
| MoE | Expert computation common candidates are grouped_matmul and large expert linear layers; dispatch/combine/router are usually not prefetch targets |
| Dense MLP | gate_up_proj/down_proj/o_proj are often candidates; but if their preceding window is already filled by another MatMul, do not force adding |
Performance test verification
Verification checklist
Run profiling before and after prefetch, and check one by one according to the following checklist:
- Whether the target operator execution time decreases:Compare the duration of the target operator before and after prefetch
- Whether the critical path is shortened:Compare the total duration or gaps/waiting time on the main stream
- Whether the dependency window is stable:The duration of preceding operators should not increase significantly
- Whether new performance bottlenecks appear:Check whether other streams show new long tails
- Whether functional correctness is maintained:Compare output consistency before and after prefetch
Success criteria
At least satisfytwo:
- Target operator duration decreases
- Critical path is shortened
- Preceding window does not degrade significantly
- No difference in functional results
Failure signals and adjustment strategies
| Failure signal | Root cause analysis | Adjustment strategy |
|---|---|---|
| The target operator does not get faster, and the preceding window instead slows down | max_size too large, contending for bandwidth |
Reduce max_size:50% → 30% → 10% |
| The critical path is not shortened, only the waiting position shifts | Improper dependency window selection | Move the prefetch position earlier or later |
| New long tails appear on multiple streams | Prefetch introduces new synchronization points | Adjust the dependency node or abandon this position |
| Dependency relationships are invalid in graph mode | Dependency node does not match the target operator | Fix the dependency node or switch to eager mode for testing |
Tuning order
- First fix the position, only tune
max_size:from 50% → 70% → 100% or 50% → 30% → 10% - Then fix
max_size, and fine-tune the dependency position:Move the dependency window earlier or later - Only finally consider stacking multiple targets:Expand after a single target is verified successfully
Common issues and fixes
| Error symptom | Root cause | Fix |
|---|---|---|
| Choosing targets without running roofline first | Lack of profiling data support | Must first use npu-roofline-analysis to confirm memory-bound |
| Selected LayerNorm/ROPE as prefetch targets | Misjudged target operators | These are usually dependency windows, not prefetch targets |
| The dependency window is also memory-bound | Improper dependency window selection | Choose a predecessor that is not memory-bound, or move prefetch earlier |
| Prefetch all weights at once | max_size too large |
Start conservatively with max_size(50%), and tune later |
| Add prefetch in multiple places at the same time | Scope of changes is too large | Start with a single target, then expand after verification |
| No switch protection | Lack of fallback mechanism | All prefetch must have enable_prefetch switch |
| Preceding window degrades but is not adjusted | Not adjusted based on profiling | Reduce max_size or adjust position, fall back when necessary |
| Compilation fails in graph mode | Dependency node is invalid | Ensure dependency nodes and target operators are on the same path |
Output requirements
Upon completion, must report:
- Which target operators were selected, and why:Based on roofline analysis results or manually specified by the user
- Key conclusions of the roofline skill:Memory-bound operator list, critical path (if automated analysis is used)
- Why this dependency window was chosen:Not memory-bound, with sufficient execution time
- Which switches and code paths were added:Configuration file → model initialization → forward call
max_sizebasis for the value:Weight dimensions, data type, conservative coefficient, or user-specified value- What validation was performed:Profiling comparison, functional validation
- What risks or follow-up tuning suggestions remain:Potential issues, next optimization directions
Files that must be delivered:
- Modified code files:Model code containing the prefetch implementation
- Test scripts:Automated test scripts used to compare baseline and optimized performance
- Performance optimization report:Markdown format, including:
- Optimization configuration and parameters
- Code modification description
- Performance test results (if model weights are available)
- Validation checklist
- Follow-up tuning suggestions
- Instructions on how to run the tests
If actual tests cannot be run(such as when model weights are missing):
- Create complete test scripts and report templates
- Explain how to run the tests
- Provide expected performance improvement analysis
Reference index
| Topic | File path |
|---|---|
| npu_prefetch wrapper function | executor/utils/common_utils.py |
| LongCat-Flash prefetch implementation | models/longcat-flash/models/modeling_longcat_flash.py |
| FFN prefetch size calculation | models/longcat-flash/models/ffn.py |
| Configuration validation logic | models/longcat-flash/models/model_setting.py |
| Configuration example | models/longcat-flash/config/README.md |
| Roofline analysis | Call npu-roofline-analysis skill |
| Graph mode adaptation | Call model-infer-graph-mode skill |