已合并
【26.2.0众测】TorchNPU 26.1.0 众测任务交付:安装验证、快速入门迁移训练、torch.compile 13项后端体验与问题反馈(gcw_8p1hhlB0) #38
【26.2.0众测】TorchNPU 26.1.0 众测任务交付:安装验证、快速入门迁移训练、torch.compile 13项后端体验与问题反馈(gcw_8p1hhlB0) #38
已合并
GMOW创建于 9月15日
共 114 个文件变更+22718-0
@@ -0,0 +1,12 @@
1+# Python 运行时产物
2+__pycache__/
3+*.pyc
4+ 
5+# torch.compile 调试输出(TORCH_COMPILE_DEBUG 产生,非交付内容)
6+torch_compile_debug/
7+fusion_result.json
8+traced_graph_cache/
9+ 
10+# 数据集与权重(不入库,生成方式见复现命令清单)
11+mnist/
12+checkpoint.pth.tar
@@ -0,0 +1,101 @@
1+# TorchNPU 26.1.0 众测任务提交材料
2+ 
3+> 提交人 gitcode 账号:[gcw_8p1hhlB0](https://gitcode.com/gcw_8p1hhlB0)
4+ 
5+## 环境信息
6+ 
7+- 硬件:Atlas 800T A2(Ascend 910B3,64GB HBM)
8+- 软件:CANN 9.1.0 / TorchNPU 2.7.1.post8 与 2.9.0.post6(均属 26.1.0 配套)/ PyTorch 2.7.1 与 2.9.0 / Python 3.11 / Ubuntu 24.04 aarch64
9+- 任务2 双环境策略:Guard Filter 示例用 PyTorch 2.9.0(guard_filter_fn 需 >=2.9.0);7 个编译后端示例用环境预装的 PyTorch 2.7.1(`run_task3.sh` 自动切换)
10+- 环境初始化:`source 补充-测试报告与原始日志/env_setup.sh`(自适应探测;从零搭建见 `补充-测试报告与原始日志/环境搭建.md`)
11+- 任务2 运行变量:`TORCHINDUCTOR_CACHE_DIR`(编译缓存)、`TORCHINDUCTOR_COMPILE_THREADS`(容器内存限制下需调低,见测试报告问题6)
12+ 
13+## 目录结构
14+ 
15+```
16+gcw_8p1hhlB0/
17+├── README.md # 本文件
18+├── 任务1-安装与快速入门/ # 任务书 4.2.1 + 4.2.2
19+│ ├── 交付件说明.txt
20+│ ├── 链接交付件.xls # Issue/PR 链接
21+│ ├── 软件安装/
22+│ │ ├── 1_pip_list截图.png
23+│ │ └── 2_torch_npu导入截图.png
24+│ └── 快速入门/
25+│ ├── 1_训练脚本.py # transfer_to_npu 自动迁移 + AMP
26+│ ├── 2_训练成功截图.png
27+│ └── 3_权重文件截图.png
28+├── 任务2-torch.compile体验与实践/ # 任务书 5.2(13 个改写示例)
29+│ ├── 交付件说明.txt
30+│ ├── 链接交付件.xls
31+│ ├── Guard Filter/ # 0_公共模块 + 1~6 脚本 + 7~12 每示例截图(含重编译消除/正确性/性能三段输出)
32+│ ├── Inductor-Triton/ # 0_公共模块 + 1_改写脚本 + 2/3 截图
33+│ ├── Inductor-MLIR/ # 额外含 4_规避方案AB对比截图
34+│ ├── Inductor-DVM/
35+│ ├── NPUGraphs/
36+│ ├── NPUGraph_EX/
37+│ ├── AOT_Eager/
38+│ └── TorchAir-GE/
39+└── 补充-测试报告与原始日志/
40+ ├── 众测测试报告.md # 7 个问题 + 8 条改进建议 + 汇总表
41+ ├── issue提交记录.md # 3 个正式 issue 链接与要点
42+ ├── 复现命令清单.md # 全部截图对应的运行命令
43+ ├── run_task3.sh # 任务2 一键运行脚本
44+ ├── env_setup.sh # 环境初始化
45+ ├── 原始日志/ # 4.2.1/4.2.2/13 个示例全部日志
46+ ├── GuardFilter-每示例截图/ # 6 个 Guard Filter 示例逐个截图
47+ ├── MLIR问题定位/ # MLIR 数值错误二分定位实验(issue #4831 证据)
48+ ├── 故障卡问题证据/ # 首日故障卡训练失败日志(测试报告问题1)
49+ └── 对照脚本/ # train_gpu.py / train_fp32.py / verify_install.py
50+```
51+ 
52+## 任务完成情况(对照任务书)
53+ 
54+| 任务 | 成功基准 | 完成情况 |
55+|---|---|---|
56+| 4.2.1 软件安装 | torch_npu 可正常导入并运行 | ✅ 官方验证命令输出 `tensor(..., device='npu:0')`(软件安装/2_torch_npu导入截图.png,日志见 补充/原始日志/4.2.1_*) |
57+| 4.2.2 快速入门 | 训练跑通、生成 checkpoint.pth.tar、无报错中断 | ✅ 自动迁移 + AMP,10 epochs(loss 0.34→0.02,train acc 99.4%),MNIST 测试集精度 98.1%、权重无 NaN(验证日志见 补充/原始日志/4.2.2_checkpoint验证.log) |
58+| 5.2.3 表1 | Guard Filter 6 个 + 后端 7 个改写运行;1 张重编译消除 + 8 张正确性 + 8 张性能截图 | ✅ 13 脚本 + 13 日志 + 截图齐全(见 任务2/ 各文件夹;截图同时含正确性与性能两段输出,详见任务2 交付件说明) |
59+ 
60+## 任务2 实测结果(resnet18, bs=8, 910B3;07~13 为 torch 2.7.1 + torch_npu 2.7.1.post8,Guard Filter 为 torch 2.9.0 + torch_npu 2.9.0.post6)
61+ 
62+| # | 后端 | 正确性 | 稳态加速比 | 截图 |
63+|---|---|---|---|---|
64+| 07 | Inductor-Triton(默认) | PASS | 0.07x(慢 14 倍)⚠️ | ![](任务2-torch.compile体验与实践/Inductor-Triton/2_正确性验证截图.png) |
65+| 08 | Inductor-MLIR | **FAIL**(max diff 9.81e-01;2.9.0 上为 NaN)⚠️ | 1.34x | ![](任务2-torch.compile体验与实践/Inductor-MLIR/2_正确性验证截图.png) |
66+| 09 | Inductor-DVM | PASS | 1.45x | ![](任务2-torch.compile体验与实践/Inductor-DVM/2_正确性验证截图.png) |
67+| 10 | NPUGraphs | PASS | 1.43x | ![](任务2-torch.compile体验与实践/NPUGraphs/2_正确性验证截图.png) |
68+| 11 | NPUGraph_EX | PASS | 1.48x | ![](任务2-torch.compile体验与实践/NPUGraph_EX/2_正确性验证截图.png) |
69+| 12 | AOT_Eager | PASS | 0.64x(符合预期) | ![](任务2-torch.compile体验与实践/AOT_Eager/2_正确性验证截图.png) |
70+| 13 | TorchAir-GE | PASS | **2.06x(最优)** | ![](任务2-torch.compile体验与实践/TorchAir-GE/2_正确性验证截图.png) |
71+ 
72+MLIR 数值错误的规避方案 A/B 对比(dynamic=True 完全修复):
73+![](任务2-torch.compile体验与实践/Inductor-MLIR/4_规避方案AB对比截图.png)
74+ 
75+Guard Filter 重编译消除(示例1,阶段A 未过滤 1→2 / 阶段B2 扩展过滤集 1→1):
76+![](任务2-torch.compile体验与实践/Guard%20Filter/7_示例1_重编译消除_正确性_性能对比截图.png)
77+ 
78+Guard Filter 结论:示例 01/03/04/05 PASS(01/03 需扩展文档过滤集)、示例 06
79+guard 条目削减 81%、**示例 02 未消除**(PyTorch 2.9.0 C 层 GlobalStateGuard 限制,
80+详见测试报告问题4)。带 ⚠️ 项为如实记录的问题反馈,非执行错误
81+(任务书:有效的问题反馈是验收通过的重要依据)。
82+ 
83+## 已提交的社区 Issue(标题带【26.2.0众测】,共 4 个;查重关联说明见 issue提交记录.md)
84+ 
85+| # | 链接 | 主题 |
86+|---|---|---|
87+| 1 | https://gitcode.com/Ascend/pytorch/issues/4831 | Inductor-MLIR 整图编译数值错误(dynamic=True 可规避) |
88+| 2 | https://gitcode.com/Ascend/pytorch/issues/4832 | Inductor-Triton 比 Eager 慢 25~79 倍(规避手段均无效) |
89+| 3 | https://gitcode.com/Ascend/pytorch/issues/4833 | Guard Filter 无法消除运行时状态类重编译 |
90+| 4 | https://gitcode.com/Ascend/pytorch/issues/5122 | PyTorch 2.9.0 Dynamo `_dynamo_dynamic_indices` 重编译崩溃 |
91+ 
92+体验问卷(平台侧)已填写。问题与改进建议详见
93+`补充-测试报告与原始日志/众测测试报告.md`(7 个问题 + 8 条建议 + 各后端汇总表)。
94+ 
95+## 复现
96+ 
97+见 `补充-测试报告与原始日志/复现命令清单.md`(环境搭建见同目录 `环境搭建.md`);任务2 一键运行:
98+`补充-测试报告与原始日志/run_task3.sh <01~13|all>`(自动按示例切换双环境:
99+01~06 用 venv PyTorch 2.9.0,07~13 用系统 PyTorch 2.7.1;并限制编译线程数,
100+避免默认 32 线程在 32GB 容器内存限制下 OOM)。
101+正确性结论确定性复现,性能数字受卡负载影响小幅波动。
@@ -0,0 +1,68 @@
1+=====================================================================
2+ 任务1 交付件存放指南
3+=====================================================================
4+ 
5+本指南详细说明任务1(软件安装 + 快速入门)所有交付件的存放位置、
6+文件命名和内容要求。
7+ 
8+---------------------------------------------------------------------
9+一、文件夹结构
10+---------------------------------------------------------------------
11+ 
12+任务1-安装与快速入门/
13+├── 交付件说明.txt ← 本文件(您正在阅读的)
14+├── 链接交付件.xls ← 填写 Issue 链接和 PR 链接
15+├── 软件安装/
16+│ ├── 1_pip_list截图.png
17+│ └── 2_torch_npu导入截图.png
18+│
19+└── 快速入门/
20+ ├── 1_训练脚本.py
21+ ├── 2_训练成功截图.png
22+ └── 3_权重文件截图.png
23+ 
24+---------------------------------------------------------------------
25+二、各交付件详细说明
26+---------------------------------------------------------------------
27+ 
28+ ★ 4.2.1 软件安装证明材料(存放于「软件安装/」文件夹)
29+ -----------------------------------------------------------------
30+ 文件1:1_pip_list截图.png
31+ - 内容:终端执行 pip list 的输出截图,可见 torch 2.7.1、
32+ torch_npu 2.7.1.post8、triton_ascend 3.2.2、torchvision 0.22.1 等包
33+ - 要求:截图清晰,包名和版本号可读
34+ 
35+ 文件2:2_torch_npu导入截图.png
36+ - 内容:终端执行官方《软件安装》"安装后验证"命令的输出截图:
37+ python3 -c "import torch;import torch_npu; a = torch.randn(3, 4).npu(); print(a + a);"
38+ - 要求:输出 tensor(..., device='npu:0') 即安装成功
39+ 
40+ ★ 4.2.2 快速入门证明材料(存放于「快速入门/」文件夹)
41+ -----------------------------------------------------------------
42+ 文件1:1_训练脚本.py
43+ - 内容:实际运行的训练脚本。GPU→NPU 迁移采用官方自动迁移方式
44+ (import torch_npu + from torch_npu.contrib import transfer_to_npu),
45+ 并按文档开启 AMP 混合精度(amp.GradScaler + amp.autocast)
46+ - 要求:脚本可独立运行(python3 1_训练脚本.py),MNIST 数据集
47+ 自动下载,训练完成后在当前目录生成 checkpoint.pth.tar
48+ - 对照:迁移前原始 GPU 脚本见
49+ 补充-测试报告与原始日志/对照脚本/train_gpu.py
50+ 
51+ 文件2:2_训练成功截图.png
52+ - 内容:训练脚本正常执行完毕的终端截图
53+ - 要求:可见 10 个 epoch 的 loss/acc 打印(loss 0.34→0.02、
54+ train acc 约 99.4%)、checkpoint saved、training finished
55+ successfully,无报错中断
56+ 
57+ 文件3:3_权重文件截图.png
58+ - 内容:生成的 checkpoint.pth.tar 权重文件的截图
59+ - 要求:可通过 ls -lh checkpoint.pth.tar 展示文件存在及大小(约 122K)
60+ - 注意:权重文件本身未随仓库提交,只需截图证明已生成。
61+ 其生成与验证(MNIST 测试集精度 98.1%、权重无 NaN)的文本证据见
62+ 补充-测试报告与原始日志/原始日志/4.2.2_checkpoint验证.log
63+ 
64+=====================================================================
65+环境:Atlas 800T A2(Ascend 910B3, 64GB)/ CANN 9.1.0 /
66+ TorchNPU 2.7.1.post8(26.1.0 配套)/ PyTorch 2.7.1 / Python 3.11
67+环境初始化:source 补充-测试报告与原始日志/env_setup.sh
68+=====================================================================
@@ -0,0 +1,103 @@
1+"""快速入门:模型迁移训练(任务书 4.2.2)。
2+ 
3+基于《快速入门》文档的 CNN 手写数字识别案例,从 GPU 脚本迁移到昇腾 NPU:
4+1. 增加自动迁移:import torch_npu + from torch_npu.contrib import transfer_to_npu
5+2. 开启 AMP 混合精度:amp.GradScaler + amp.autocast
6+原始 GPU 脚本见 train_gpu.py,差异可用 diff train_gpu.py train.py 查看。
7+"""
8+import time
9+ 
10+import torch
11+import torch.nn as nn
12+from torch.utils.data import Dataset, DataLoader
13+import torchvision
14+ 
15+import torch_npu
16+from torch_npu.contrib import transfer_to_npu # 开启自动迁移
17+from torch_npu.npu import amp # 导入AMP模块
18+ 
19+# 初始化运行device(transfer_to_npu 已自动将 cuda:0 映射到 NPU)
20+device = torch.device('cuda:0')
21+ 
22+ 
23+# 定义模型网络
24+class CNN(nn.Module):
25+ def __init__(self):
26+ super(CNN, self).__init__()
27+ self.net = nn.Sequential(
28+ # 卷积层
29+ nn.Conv2d(in_channels=1, out_channels=16,
30+ kernel_size=(3, 3),
31+ stride=(1, 1),
32+ padding=1),
33+ # 池化层
34+ nn.MaxPool2d(kernel_size=2),
35+ # 卷积层
36+ nn.Conv2d(16, 32, 3, 1, 1),
37+ # 池化层
38+ nn.MaxPool2d(2),
39+ # 将多维输入一维化
40+ nn.Flatten(),
41+ nn.Linear(32*7*7, 16),
42+ # 激活函数
43+ nn.ReLU(),
44+ nn.Linear(16, 10)
45+ )
46+ 
47+ def forward(self, x):
48+ return self.net(x)
49+ 
50+ 
51+# 下载数据集
52+train_data = torchvision.datasets.MNIST(
53+ root='mnist',
54+ download=True,
55+ train=True,
56+ transform=torchvision.transforms.ToTensor()
57+)
58+ 
59+# 定义训练相关参数
60+batch_size = 64
61+model = CNN().to(device) # 定义模型
62+train_dataloader = DataLoader(train_data, batch_size=batch_size) # 定义DataLoader
63+loss_func = nn.CrossEntropyLoss().to(device) # 定义损失函数
64+optimizer = torch.optim.SGD(model.parameters(), lr=0.1) # 定义优化器
65+scaler = amp.GradScaler() # 在模型、优化器定义之后,定义GradScaler
66+epochs = 10 # 设置循环次数
67+ 
68+print("device:", device, "| torch_npu:", torch_npu.__version__)
69+print("start training: epochs =", epochs, ", batch_size =", batch_size,
70+ ", iters/epoch =", len(train_dataloader))
71+ 
72+# 设置循环
73+for epoch in range(epochs):
74+ epoch_loss = 0.0
75+ correct = 0
76+ total = 0
77+ epoch_start = time.time()
78+ for imgs, labels in train_dataloader:
79+ imgs = imgs.to(device) # 把img数据放到指定NPU上
80+ labels = labels.to(device) # 把label数据放到指定NPU上
81+ with amp.autocast():
82+ outputs = model(imgs) # 前向计算
83+ loss = loss_func(outputs, labels) # 损失函数计算
84+ optimizer.zero_grad()
85+ # 进行反向传播前后的loss缩放、参数更新
86+ scaler.scale(loss).backward() # loss缩放并反向传播
87+ scaler.step(optimizer) # 更新参数(自动unscaling)
88+ scaler.update() # 基于动态Loss Scale更新loss_scaling系数
89+ epoch_loss += loss.item()
90+ correct += (outputs.argmax(dim=1) == labels).sum().item()
91+ total += labels.size(0)
92+ print(f"epoch {epoch + 1}/{epochs} | loss {epoch_loss / len(train_dataloader):.4f} "
93+ f"| train acc {correct / total:.4f} | time {time.time() - epoch_start:.2f}s")
94+ 
95+# 定义保存模型
96+torch.save({
97+ 'epoch': 10,
98+ 'arch': CNN,
99+ 'state_dict': model.state_dict(),
100+ 'optimizer': optimizer.state_dict(),
101+}, 'checkpoint.pth.tar')
102+print("checkpoint saved: checkpoint.pth.tar")
103+print("training finished successfully")
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,22 @@
1+"""任务3 示例12:AOT_Eager 后端。
2+ 
3+对应《AOT_Eager》文档调用示例,模型替换为 TorchBench 清单中的 resnet18。
4+核心:backend="aot_eager"(仅验证图捕获正确性,不做优化,作为基准对照)。
5+"""
6+import torch
7+import torch_npu
8+from tc_common import build_model, build_input, correctness_check, perf_compare
9+ 
10+print("=" * 70)
11+print("示例12:AOT_Eager 后端")
12+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
13+print("=" * 70)
14+ 
15+model = build_model()
16+x = build_input()
17+ 
18+compiled = torch.compile(model, backend="aot_eager")
19+ 
20+ok, diff = correctness_check(model, compiled, x, "示例12-aot_eager")
21+perf_compare(model, compiled, x, "示例12-aot_eager")
22+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,150 @@
1+"""任务3 示例01:Guard Filter 示例1 —— 过滤字典版本 guard。
2+ 
3+对应《Guard Filter》文档示例1(DICT_VERSION/DICT_KEYS/DICT_KEYS_MATCH/DICT_CONTAINS),
4+模型替换为 TorchBench 清单中的 resnet18。通过 KV-cache 式 dict 参数内 mask 值
5+None ↔ 全1 tensor 切换(两者语义等价,模拟 prefill/decode 交替)触发重编译。
6+ 
7+实测发现(已作为问题反馈):
8+- PyTorch 2.9.0 中 dict 读取会被常量特化,实际失效的 guard 是 dict 内值的
9+ None 检查(guard_type=CONSTANT_MATCH, name=attn['mask']),不在文档示例1
10+ 的 DICT_* 过滤集内,因此仅用该过滤集无法消除重编译;
11+- 将过滤集扩展为「DICT_* + 按名丢弃 None 检查」后重编译消除成功。
12+"""
13+import torch
14+import torch_npu
15+import torch.nn as nn
16+from tc_common import (build_input, correctness_check, perf_compare,
17+ count_graphs, reset_dynamo)
18+from tc_common import resnet18
19+ 
20+_DICT_GUARD_TYPES = frozenset({
21+ "DICT_VERSION", "DICT_KEYS", "DICT_KEYS_MATCH", "DICT_CONTAINS",
22+})
23+ 
24+ 
25+def filter_dict_guards(entries):
26+ """文档示例1的过滤函数(仅过滤 DICT_* 类 guard)。"""
27+ return [entry.guard_type not in _DICT_GUARD_TYPES for entry in entries]
28+ 
29+ 
30+def filter_dict_guards_extended(entries):
31+ """扩展过滤:DICT_* + 按名丢弃 dict 内值的 None 检查(CONSTANT_MATCH)。"""
32+ keep = []
33+ for entry in entries:
34+ if entry.guard_type in _DICT_GUARD_TYPES:
35+ keep.append(False)
36+ elif entry.guard_type == "CONSTANT_MATCH" and entry.name == "attn['mask']" and entry.value is None:
37+ keep.append(False)
38+ else:
39+ keep.append(True)
40+ return keep
41+ 
42+ 
43+class ResNet18WithAttnDict(nn.Module):
44+ """resnet18 + KV-cache 式 dict 参数(attn['mask'] None ↔ 全1 mask 语义等价)。"""
45+ 
46+ def __init__(self, backbone):
47+ super().__init__()
48+ self.backbone = backbone
49+ 
50+ def forward(self, x, attn):
51+ out = self.backbone(x)
52+ mask = attn["mask"] # 模拟 HF 模型从 dict 读取 attention_mask
53+ if mask is not None:
54+ out = out * mask # 全1 mask 与 None 语义等价
55+ return out
56+ 
57+ 
58+torch.manual_seed(42)
59+model = ResNet18WithAttnDict(resnet18()).npu().eval()
60+x = build_input()
61+ones = torch.ones(x.size(0), 1000, device="npu")
62+ 
63+print("=" * 70)
64+print("示例01:过滤字典版本 guard(DICT_*)")
65+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
66+print("=" * 70)
67+ 
68+# ---------- 阶段A:不开启 Guard Filter(对照) ----------
69+reset_dynamo()
70+attn = {"mask": None}
71+compiled_plain = torch.compile(model)
72+with torch.no_grad():
73+ compiled_plain(x, attn)
74+g1 = count_graphs()
75+attn["mask"] = ones # dict 内值变化(version bump + None→tensor)
76+with torch.no_grad():
77+ compiled_plain(x, attn)
78+g2 = count_graphs()
79+print(f"\n[阶段A|未过滤] 首次调用后图数: {g1},dict值切换后再调用后图数: {g2}")
80+print(f"[阶段A|未过滤] 重编译: {'发生' if g2 > g1 else '未发生'}")
81+ 
82+# ---------- 阶段B:文档示例1的 DICT_* 过滤集 ----------
83+reset_dynamo()
84+attn = {"mask": None}
85+compiled_doc = torch.compile(model, options={"guard_filter_fn": filter_dict_guards})
86+with torch.no_grad():
87+ compiled_doc(x, attn)
88+g3 = count_graphs()
89+attn["mask"] = ones
90+with torch.no_grad():
91+ compiled_doc(x, attn)
92+g4 = count_graphs()
93+print(f"\n[阶段B|文档DICT_*过滤集] 首次调用后图数: {g3},dict值切换后图数: {g4}")
94+print(f"[阶段B|文档DICT_*过滤集] 重编译: {'已消除' if g4 == g3 else '仍发生 (实际失效guard为 dict内值的None检查, 类型CONSTANT_MATCH, 不在DICT_*集内)'}")
95+ 
96+# ---------- 阶段B2:扩展过滤集(DICT_* + 按名丢弃None检查) ----------
97+reset_dynamo()
98+attn = {"mask": None}
99+compiled_ext = torch.compile(model, options={"guard_filter_fn": filter_dict_guards_extended})
100+with torch.no_grad():
101+ ref = compiled_ext(x, attn)
102+g5 = count_graphs()
103+attn["mask"] = ones
104+with torch.no_grad():
105+ out_ext = compiled_ext(x, attn)
106+g6 = count_graphs()
107+same = torch.allclose(ref.cpu(), out_ext.cpu(), atol=1e-4)
108+print(f"\n[阶段B2|扩展过滤集] 首次调用后图数: {g5},dict值切换后图数: {g6}")
109+print(f"[阶段B2|扩展过滤集] 重编译: {'已消除' if g6 == g5 else '仍发生'},输出一致: {same}")
110+print(f"[结论] Guard Filter 重编译消除: {'PASS' if (g2 > g1 and g6 == g5 and same) else 'FAIL'}")
111+ 
112+# ---------- 阶段C:fail_on_recompile 严格验证 ----------
113+try:
114+ with torch.compiler.set_stance("fail_on_recompile"):
115+ attn["mask"] = None
116+ with torch.no_grad():
117+ compiled_ext(x, attn)
118+ print("[阶段C|fail_on_recompile] 未抛异常,重编译确实已消除: PASS")
119+except Exception as exc:
120+ print(f"[阶段C|fail_on_recompile] 抛出异常: {type(exc).__name__}: FAIL")
121+ 
122+# ---------- 正确性 & 性能 ----------
123+import time # noqa: E402
124+ 
125+with torch.no_grad():
126+ eager_out = model(x, {"mask": None})
127+ compiled_out = compiled_ext(x, {"mask": None})
128+diff = float((eager_out.cpu() - compiled_out.cpu()).abs().max())
129+ok = torch.allclose(eager_out.cpu(), compiled_out.cpu(), atol=1e-3)
130+print(f"\n[正确性|示例01-guard_filter_dict] eager vs compiled 最大差异: {diff:.3e} (allclose: {ok})")
131+ 
132+with torch.no_grad():
133+ start = time.time(); model(x, {"mask": None}); eager_time = time.time() - start
134+ start = time.time(); compiled_ext(x, {"mask": None}); compile_time = time.time() - start
135+ for _ in range(3):
136+ model(x, {"mask": None}); compiled_ext(x, {"mask": None})
137+ torch.npu.synchronize()
138+ start = time.time()
139+ for _ in range(20):
140+ model(x, {"mask": None})
141+ torch.npu.synchronize()
142+ eager_avg = (time.time() - start) / 20
143+ start = time.time()
144+ for _ in range(20):
145+ compiled_ext(x, {"mask": None})
146+ torch.npu.synchronize()
147+ compiled_avg = (time.time() - start) / 20
148+print(f"[性能|示例01-guard_filter_dict] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
149+print(f"[性能|示例01-guard_filter_dict] 稳态均值(20次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s, 加速比: {eager_avg / compiled_avg:.2f}x")
150+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,85 @@
1+"""任务3 示例02:Guard Filter 示例2 —— 过滤运行时状态 guard。
2+ 
3+对应《Guard Filter》文档示例2(GRAD_MODE/TORCH_FUNCTION_STATE/GLOBAL_STATE/
4+DEFAULT_DEVICE/DETERMINISTIC_ALGORITHMS/AUTOCAST_STATE/FSDP_TRAINING_STATE),
5+模型替换为 TorchBench 清单中的 resnet18。
6+ 
7+实测发现(重要,已作为问题反馈):
8+- PyTorch 2.9.0 中运行时状态(grad_mode、deterministic_algorithms 等)的实际
9+ 校验走 C 层 GlobalStateGuard(见 torch/_dynamo/guards.py:2433 注释 "we always
10+ guard on this via GlobalStateGuard()"),该检查不经过 guard_filter_fn 通道,
11+ 因此文档示例2的过滤集无法消除此类重编译;
12+- 重编译日志显示失效 guard 为 "GLOBAL_STATE changed: deterministic_algorithms",
13+ 证明是 C 层检查触发,而非 Python 层 guard 条目;
14+- 本示例使用 deterministic_algorithms 开关作为触发器(与 grad_mode 同属
15+ GLOBAL_STATE 家族,且避免 enable_grad 触发反向图编译的额外内存开销)。
16+"""
17+import torch
18+import torch_npu
19+from tc_common import (build_model, build_input, correctness_check,
20+ perf_compare, count_graphs, reset_dynamo)
21+ 
22+_RUNTIME_STATE_GUARD_TYPES = frozenset({
23+ "GRAD_MODE", "TORCH_FUNCTION_STATE", "GLOBAL_STATE",
24+ "DEFAULT_DEVICE", "DETERMINISTIC_ALGORITHMS", "AUTOCAST_STATE",
25+ "FSDP_TRAINING_STATE",
26+})
27+ 
28+ 
29+def filter_runtime_state_guards(entries):
30+ return [entry.guard_type not in _RUNTIME_STATE_GUARD_TYPES for entry in entries]
31+ 
32+ 
33+model = build_model()
34+x = build_input()
35+ 
36+print("=" * 70)
37+print("示例02:过滤运行时状态 guard(GLOBAL_STATE 家族)")
38+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
39+print("触发器: torch.use_deterministic_algorithms 开关翻转")
40+print("=" * 70)
41+ 
42+# ---------- 阶段A:不开启 Guard Filter(对照) ----------
43+reset_dynamo()
44+compiled_plain = torch.compile(model)
45+with torch.no_grad():
46+ compiled_plain(x)
47+g1 = count_graphs()
48+torch.use_deterministic_algorithms(True) # 翻转运行时状态
49+with torch.no_grad():
50+ compiled_plain(x)
51+g2 = count_graphs()
52+torch.use_deterministic_algorithms(False)
53+print(f"\n[阶段A|未过滤] 首次调用后图数: {g1},状态翻转后图数: {g2}")
54+print(f"[阶段A|未过滤] 重编译: {'发生 (GLOBAL_STATE guard 失效)' if g2 > g1 else '未发生'}")
55+ 
56+# ---------- 阶段B:开启文档示例2的运行时状态过滤集 ----------
57+reset_dynamo()
58+compiled_filtered = torch.compile(model, options={"guard_filter_fn": filter_runtime_state_guards})
59+with torch.no_grad():
60+ compiled_filtered(x)
61+g3 = count_graphs()
62+torch.use_deterministic_algorithms(True)
63+with torch.no_grad():
64+ compiled_filtered(x)
65+g4 = count_graphs()
66+torch.use_deterministic_algorithms(False)
67+print(f"\n[阶段B|已过滤] 首次调用后图数: {g3},状态翻转后图数: {g4}")
68+print(f"[阶段B|已过滤] 重编译: {'已消除' if g4 == g3 else '仍发生 (C 层 GlobalStateGuard 不经 guard_filter_fn 通道, 见脚本 docstring)'}")
69+print(f"[结论] 运行时状态重编译消除: {'PASS' if (g2 > g1 and g4 == g3) else 'FAIL (PyTorch 2.9.0 限制, 已作为问题反馈)'}")
70+ 
71+# ---------- 阶段C:fail_on_recompile 严格验证(预期抛异常佐证 C 层检查) ----------
72+try:
73+ torch.use_deterministic_algorithms(True)
74+ with torch.compiler.set_stance("fail_on_recompile"):
75+ with torch.no_grad():
76+ compiled_filtered(x)
77+ torch.use_deterministic_algorithms(False)
78+ print("[阶段C|fail_on_recompile] 未抛异常: PASS")
79+except Exception as exc:
80+ torch.use_deterministic_algorithms(False)
81+ print(f"[阶段C|fail_on_recompile] 抛出异常 {type(exc).__name__}: 佐证重编译由 C 层 GlobalStateGuard 触发, guard_filter_fn 无法拦截")
82+ 
83+# ---------- 正确性 & 性能 ----------
84+ok, diff = correctness_check(model, compiled_filtered, x, "示例02-guard_filter_runtime_state")
85+perf_compare(model, compiled_filtered, x, "示例02-guard_filter_runtime_state")
@@ -0,0 +1,151 @@
1+"""任务3 示例03:Guard Filter 示例3 —— 组合多类 guard 过滤。
2+ 
3+对应《Guard Filter》文档示例3(字典版本 + 可选类型 + hasattr + 运行时状态
4+一次性过滤),模型替换为 TorchBench 清单中的 resnet18。通过可选参数 mask
5+None ↔ 全1 tensor 切换(语义等价,模拟 attention_mask prefill/decode 交替)
6+触发重编译。
7+ 
8+实测发现(已作为问题反馈):
9+- 失效 guard 为 mask=None 的 None 检查(guard_type=CONSTANT_MATCH, name=mask),
10+ 不在文档示例3的组合过滤集内,因此该过滤集单独使用无法消除重编译;
11+- 将组合集扩展为「组合集 + 按名丢弃 mask=None 检查」后重编译消除成功。
12+"""
13+import torch
14+import torch_npu
15+import torch.nn as nn
16+from tc_common import (build_input, count_graphs, reset_dynamo)
17+from tc_common import resnet18
18+ 
19+_FILTER_GUARD_TYPES = frozenset({
20+ # 字典版本
21+ "DICT_VERSION", "DICT_KEYS", "DICT_KEYS_MATCH", "DICT_CONTAINS",
22+ # 可选类型
23+ "TYPE_MATCH", "OPTIONAL_TENSOR",
24+ # hasattr
25+ "HASATTR", "NOT_PRESENT_IN_GENERIC_DICT",
26+ # 运行时状态
27+ "GRAD_MODE", "TORCH_FUNCTION_STATE", "GLOBAL_STATE",
28+ "DEFAULT_DEVICE", "DETERMINISTIC_ALGORITHMS", "AUTOCAST_STATE",
29+ "FSDP_TRAINING_STATE",
30+})
31+ 
32+ 
33+def npu_guard_filter(entries):
34+ """文档示例3的组合过滤函数。"""
35+ return [entry.guard_type not in _FILTER_GUARD_TYPES for entry in entries]
36+ 
37+ 
38+def npu_guard_filter_extended(entries):
39+ """扩展组合过滤:组合集 + 按名丢弃 mask=None 的 None 检查(CONSTANT_MATCH)。"""
40+ keep = []
41+ for entry in entries:
42+ if entry.guard_type in _FILTER_GUARD_TYPES:
43+ keep.append(False)
44+ elif entry.guard_type == "CONSTANT_MATCH" and entry.name == "mask" and entry.value is None:
45+ keep.append(False)
46+ else:
47+ keep.append(True)
48+ return keep
49+ 
50+ 
51+class ResNet18WithOptionalMask(nn.Module):
52+ """resnet18 + 可选 mask 参数(None 与全1 mask 语义等价)。"""
53+ 
54+ def __init__(self, backbone):
55+ super().__init__()
56+ self.backbone = backbone
57+ 
58+ def forward(self, x, mask=None):
59+ out = self.backbone(x)
60+ if mask is not None:
61+ out = out * mask # 全1 mask 与 None 语义等价
62+ return out
63+ 
64+ 
65+torch.manual_seed(42)
66+model = ResNet18WithOptionalMask(resnet18()).npu().eval()
67+x = build_input()
68+ones = torch.ones(x.size(0), 1000, device="npu")
69+ 
70+print("=" * 70)
71+print("示例03:组合多类 guard 过滤")
72+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
73+print("=" * 70)
74+ 
75+# ---------- 阶段A:不开启 Guard Filter(对照) ----------
76+reset_dynamo()
77+compiled_plain = torch.compile(model)
78+with torch.no_grad():
79+ compiled_plain(x)
80+g1 = count_graphs()
81+with torch.no_grad():
82+ compiled_plain(x, ones) # mask None → 全1 tensor
83+g2 = count_graphs()
84+print(f"\n[阶段A|未过滤] 首次调用后图数: {g1},mask切换后图数: {g2}")
85+print(f"[阶段A|未过滤] 重编译: {'发生' if g2 > g1 else '未发生'}")
86+ 
87+# ---------- 阶段B:文档示例3的组合过滤集 ----------
88+reset_dynamo()
89+compiled_doc = torch.compile(model, options={"guard_filter_fn": npu_guard_filter})
90+with torch.no_grad():
91+ compiled_doc(x)
92+g3 = count_graphs()
93+with torch.no_grad():
94+ compiled_doc(x, ones)
95+g4 = count_graphs()
96+print(f"\n[阶段B|文档组合过滤集] 首次调用后图数: {g3},mask切换后图数: {g4}")
97+print(f"[阶段B|文档组合过滤集] 重编译: {'已消除' if g4 == g3 else '仍发生 (失效guard为 mask=None 的 CONSTANT_MATCH, 不在组合集内)'}")
98+ 
99+# ---------- 阶段B2:扩展组合过滤集 ----------
100+reset_dynamo()
101+compiled_ext = torch.compile(model, options={"guard_filter_fn": npu_guard_filter_extended})
102+with torch.no_grad():
103+ ref = compiled_ext(x)
104+g5 = count_graphs()
105+with torch.no_grad():
106+ out_ext = compiled_ext(x, ones)
107+g6 = count_graphs()
108+same = torch.allclose(ref.cpu(), out_ext.cpu(), atol=1e-4)
109+print(f"\n[阶段B2|扩展组合过滤集] 首次调用后图数: {g5},mask切换后图数: {g6}")
110+print(f"[阶段B2|扩展组合过滤集] 重编译: {'已消除' if g6 == g5 else '仍发生'},输出一致: {same}")
111+print(f"[结论] 组合过滤重编译消除: {'PASS' if (g2 > g1 and g6 == g5 and same) else 'FAIL'}")
112+ 
113+# ---------- 阶段C:fail_on_recompile 严格验证 ----------
114+try:
115+ with torch.compiler.set_stance("fail_on_recompile"):
116+ with torch.no_grad():
117+ compiled_ext(x) # mask=None
118+ compiled_ext(x, ones) # mask=全1
119+ print("[阶段C|fail_on_recompile] 未抛异常: PASS")
120+except Exception as exc:
121+ print(f"[阶段C|fail_on_recompile] 抛出异常: {type(exc).__name__}: FAIL")
122+ 
123+# ---------- 正确性 & 性能 ----------
124+import time # noqa: E402
125+ 
126+with torch.no_grad():
127+ eager_out = model(x)
128+ compiled_out = compiled_ext(x)
129+diff = float((eager_out.cpu() - compiled_out.cpu()).abs().max())
130+ok = torch.allclose(eager_out.cpu(), compiled_out.cpu(), atol=1e-3)
131+print(f"\n[正确性|示例03-guard_filter_combined] eager vs compiled 最大差异: {diff:.3e} (allclose: {ok})")
132+ 
133+with torch.no_grad():
134+ start = time.time(); model(x); eager_time = time.time() - start
135+ start = time.time(); compiled_ext(x); compile_time = time.time() - start
136+ for _ in range(3):
137+ model(x); compiled_ext(x)
138+ torch.npu.synchronize()
139+ start = time.time()
140+ for _ in range(20):
141+ model(x)
142+ torch.npu.synchronize()
143+ eager_avg = (time.time() - start) / 20
144+ start = time.time()
145+ for _ in range(20):
146+ compiled_ext(x)
147+ torch.npu.synchronize()
148+ compiled_avg = (time.time() - start) / 20
149+print(f"[性能|示例03-guard_filter_combined] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
150+print(f"[性能|示例03-guard_filter_combined] 稳态均值(20次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s, 加速比: {eager_avg / compiled_avg:.2f}x")
151+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,106 @@
1+"""任务3 示例04:Guard Filter 示例4 —— 按变量名或属性过滤。
2+ 
3+对应《Guard Filter》文档示例4(按变量名精确过滤,entry.name 匹配 + value
4+为 None 时丢弃)。文档原示例是对函数使用 @torch.compile 装饰器,此处将
5+模型替换为 TorchBench 清单中的 resnet18 的前向包装:f(x, mask=None),
6+mask 为可选 attention mask(None 与全1 mask 语义等价),模拟 prefill
7+(mask=None)与 decode(mask=tensor)交替场景。
8+"""
9+import torch
10+import torch_npu
11+import torch.nn as nn
12+from tc_common import build_input, count_graphs, reset_dynamo
13+from tc_common import resnet18
14+ 
15+ 
16+def filter_by_name(entries):
17+ return [not (entry.name == "mask" and entry.value is None) for entry in entries]
18+ 
19+ 
20+class ResNet18WithOptionalMask(nn.Module):
21+ def __init__(self, backbone):
22+ super().__init__()
23+ self.backbone = backbone
24+ 
25+ def forward(self, x, mask=None):
26+ out = self.backbone(x)
27+ if mask is not None:
28+ out = out * mask # 全1 mask 与 None 语义等价
29+ return out
30+ 
31+ 
32+torch.manual_seed(42)
33+model = ResNet18WithOptionalMask(resnet18()).npu().eval()
34+x = build_input()
35+ones = torch.ones(x.size(0), 1000, device="npu")
36+ 
37+print("=" * 70)
38+print("示例04:按变量名/属性过滤(mask=None 交替场景)")
39+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
40+print("=" * 70)
41+ 
42+# ---------- 阶段A:不开启 Guard Filter(对照):None -> tensor 触发重编译 ----------
43+reset_dynamo()
44+compiled_plain = torch.compile(model)
45+with torch.no_grad():
46+ compiled_plain(x) # 先以 mask=None 编译
47+g1 = count_graphs()
48+with torch.no_grad():
49+ compiled_plain(x, ones) # 切到 mask=全1
50+g2 = count_graphs()
51+print(f"\n[阶段A|未过滤] mask=None后图数: {g1},mask=tensor后图数: {g2}")
52+print(f"[阶段A|未过滤] 类型交替触发重编译: {'发生' if g2 > g1 else '未发生'}")
53+ 
54+# ---------- 阶段B:按名称过滤 ----------
55+reset_dynamo()
56+compiled_filtered = torch.compile(model, options={"guard_filter_fn": filter_by_name})
57+with torch.no_grad():
58+ ref = compiled_filtered(x) # mask=None 编译
59+g3 = count_graphs()
60+with torch.no_grad():
61+ out = compiled_filtered(x, ones) # mask=全1 → 不应重编译
62+g4 = count_graphs()
63+same = torch.allclose(ref.cpu(), out.cpu(), atol=1e-4)
64+print(f"\n[阶段B|按名过滤] mask=None后图数: {g3},mask=tensor后图数: {g4}")
65+print(f"[阶段B|按名过滤] 类型交替重编译: {'已消除' if g4 == g3 else '仍发生'},输出一致: {same}")
66+print(f"[结论] 按名过滤重编译消除: {'PASS' if (g2 > g1 and g4 == g3 and same) else 'FAIL'}")
67+ 
68+# ---------- 阶段C:fail_on_recompile 严格验证 ----------
69+try:
70+ with torch.compiler.set_stance("fail_on_recompile"):
71+ with torch.no_grad():
72+ compiled_filtered(x)
73+ compiled_filtered(x, ones)
74+ print("[阶段C|fail_on_recompile] 未抛异常: PASS")
75+except Exception as exc:
76+ print(f"[阶段C|fail_on_recompile] 抛出异常: {type(exc).__name__}: FAIL")
77+ 
78+# ---------- 正确性 & 性能(mask=None 路径,与 eager 对比) ----------
79+import time # noqa: E402
80+ 
81+with torch.no_grad():
82+ eager_out = model(x)
83+ compiled_out = compiled_filtered(x)
84+diff = float((eager_out.cpu() - compiled_out.cpu()).abs().max())
85+ok = torch.allclose(eager_out.cpu(), compiled_out.cpu(), atol=1e-3)
86+print(f"\n[正确性|示例04] eager vs compiled 最大差异: {diff:.3e} (allclose: {ok})")
87+ 
88+with torch.no_grad():
89+ start = time.time(); model(x); eager_time = time.time() - start
90+ start = time.time(); compiled_filtered(x); compile_time = time.time() - start
91+ for _ in range(3):
92+ model(x); compiled_filtered(x)
93+ torch.npu.synchronize()
94+ start = time.time()
95+ for _ in range(20):
96+ model(x)
97+ torch.npu.synchronize()
98+ eager_avg = (time.time() - start) / 20
99+ start = time.time()
100+ for _ in range(20):
101+ compiled_filtered(x)
102+ torch.npu.synchronize()
103+ compiled_avg = (time.time() - start) / 20
104+print(f"[性能|示例04] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
105+print(f"[性能|示例04] 稳态均值(20次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s, 加速比: {eager_avg / compiled_avg:.2f}x")
106+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,73 @@
1+"""任务3 示例05:Guard Filter 示例5 —— 过滤全局变量 guard。
2+ 
3+对应《Guard Filter》文档示例5(过滤 is_global 的 guard 条目),模型替换为
4+TorchBench 清单中的 resnet18。前向依赖一个模块级全局开关 DEBUG_MODE,
5+编译后修改该全局变量,验证未过滤时触发重编译、过滤后消除。
6+"""
7+import torch
8+import torch_npu
9+import torch.nn as nn
10+from tc_common import (build_input, correctness_check, perf_compare,
11+ count_graphs, reset_dynamo)
12+from tc_common import resnet18
13+ 
14+DEBUG_MODE = False # 模块级全局开关(guard 的 is_global 目标)
15+ 
16+ 
17+class ResNet18WithGlobalSwitch(nn.Module):
18+ def __init__(self, backbone):
19+ super().__init__()
20+ self.backbone = backbone
21+ 
22+ def forward(self, x):
23+ out = self.backbone(x)
24+ if DEBUG_MODE: # 引用全局变量,产生 is_global guard
25+ out = out * 1.0
26+ return out
27+ 
28+ 
29+def filter_globals(entries):
30+ return [not entry.is_global for entry in entries]
31+ 
32+ 
33+torch.manual_seed(42)
34+model = ResNet18WithGlobalSwitch(resnet18()).npu().eval()
35+x = build_input()
36+ 
37+print("=" * 70)
38+print("示例05:过滤全局变量 guard(is_global)")
39+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
40+print("=" * 70)
41+ 
42+# ---------- 阶段A:不开启 Guard Filter(对照) ----------
43+reset_dynamo()
44+compiled_plain = torch.compile(model)
45+with torch.no_grad():
46+ compiled_plain(x)
47+g1 = count_graphs()
48+import __main__ # noqa: E402
49+__main__.DEBUG_MODE = True # 修改全局变量
50+with torch.no_grad():
51+ compiled_plain(x)
52+g2 = count_graphs()
53+print(f"\n[阶段A|未过滤] 首次调用后图数: {g1},全局变量修改后图数: {g2}")
54+print(f"[阶段A|未过滤] 全局变量修改触发重编译: {'发生' if g2 > g1 else '未发生'}")
55+ 
56+# ---------- 阶段B:过滤全局 guard ----------
57+reset_dynamo()
58+__main__.DEBUG_MODE = False
59+compiled_filtered = torch.compile(model, options={"guard_filter_fn": filter_globals})
60+with torch.no_grad():
61+ compiled_filtered(x)
62+g3 = count_graphs()
63+__main__.DEBUG_MODE = True
64+with torch.no_grad():
65+ compiled_filtered(x)
66+g4 = count_graphs()
67+print(f"\n[阶段B|已过滤] 首次调用后图数: {g3},全局变量修改后图数: {g4}")
68+print(f"[阶段B|已过滤] 全局变量修改重编译: {'已消除' if g4 == g3 else '仍发生'}")
69+print(f"[结论] 全局guard过滤重编译消除: {'PASS' if (g2 > g1 and g4 == g3) else 'FAIL'}")
70+ 
71+# ---------- 正确性 & 性能 ----------
72+ok, diff = correctness_check(model, compiled_filtered, x, "示例05-guard_filter_globals")
73+perf_compare(model, compiled_filtered, x, "示例05-guard_filter_globals")
@@ -0,0 +1,70 @@
1+"""任务3 示例06:Guard Filter 示例6 —— 结合内置 helper 使用。
2+ 
3+对应《Guard Filter》文档示例6(torch.compiler.skip_guard_on_inbuilt_nn_modules_unsafe),
4+模型替换为 TorchBench 清单中的 resnet18(全部由内置 Conv2d/BatchNorm2d/
5+Linear/ReLU 模块组成)。
6+ 
7+实测说明:该 helper 的作用是丢弃「内置 nn.Module 属性来源」的 guard 条目
8+(如权重张量的 TENSOR_MATCH),收益是每次调用时需校验的 guard 数量减少
9+(guard 检查延迟降低),本示例通过统计 filter 收到的 guard 条目数与被丢弃
10+数进行量化;同时用 fail_on_recompile 验证稳态无重编译。
11+"""
12+import torch
13+import torch_npu
14+from tc_common import (build_model, build_input, correctness_check,
15+ perf_compare, count_graphs, reset_dynamo)
16+ 
17+print("=" * 70)
18+print("示例06:内置 helper(skip_guard_on_inbuilt_nn_modules_unsafe)")
19+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
20+print("=" * 70)
21+ 
22+model = build_model()
23+x = build_input()
24+ 
25+# ---------- 阶段A:不开启(对照),统计 guard 条目数 ----------
26+reset_dynamo()
27+stats = {"total": 0, "dropped": 0}
28+ 
29+def counting_filter_keep_all(entries):
30+ stats["total"] += len(entries)
31+ return [True] * len(entries)
32+ 
33+compiled_plain = torch.compile(model, options={"guard_filter_fn": counting_filter_keep_all})
34+with torch.no_grad():
35+ compiled_plain(x)
36+g1 = count_graphs()
37+print(f"\n[阶段A|未过滤] 编译时 guard 条目总数: {stats['total']}")
38+ 
39+# ---------- 阶段B:内置 helper,统计被丢弃的 guard 数 ----------
40+reset_dynamo()
41+stats_b = {"total": 0, "dropped": 0}
42+ 
43+def counting_helper(entries):
44+ drops = torch.compiler.skip_guard_on_inbuilt_nn_modules_unsafe(entries)
45+ stats_b["total"] += len(entries)
46+ stats_b["dropped"] += sum(1 for k in drops if not k)
47+ return drops
48+ 
49+compiled_helper = torch.compile(model, options={"guard_filter_fn": counting_helper})
50+with torch.no_grad():
51+ compiled_helper(x)
52+g2 = count_graphs()
53+reduction = stats_b["dropped"] / stats_b["total"] * 100 if stats_b["total"] else 0
54+print(f"[阶段B|内置helper] guard 条目总数: {stats_b['total']}, 被丢弃: {stats_b['dropped']} ({reduction:.0f}% 削减)")
55+print(f"[结论] 内置helper guard 削减: {'PASS' if stats_b['dropped'] > 0 else 'FAIL'}")
56+ 
57+# ---------- 阶段C:fail_on_recompile 稳态验证 ----------
58+try:
59+ with torch.compiler.set_stance("fail_on_recompile"):
60+ with torch.no_grad():
61+ compiled_helper(x)
62+ compiled_helper(x)
63+ print("[阶段C|fail_on_recompile] 稳态多次调用未触发重编译: PASS")
64+except Exception as exc:
65+ print(f"[阶段C|fail_on_recompile] 抛出异常: {type(exc).__name__}: FAIL")
66+ 
67+# ---------- 正确性 & 性能 ----------
68+ok, diff = correctness_check(model, compiled_helper, x, "示例06-guard_filter_builtin_helper")
69+perf_compare(model, compiled_helper, x, "示例06-guard_filter_builtin_helper")
70+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,27 @@
1+"""任务3 示例09:Inductor 后端 - DVM 模式。
2+ 
3+对应《Inductor》文档 DVM 调用示例,模型替换为 TorchBench 清单中的
4+resnet18。开启方式:backend="inductor", options={"npu_backend": "dvm"}
5+(同时设置 TORCHINDUCTOR_NPU_BACKEND=dvm 环境变量)。
6+"""
7+import os
8+os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm"
9+ 
10+import torch
11+import torch_npu
12+import torch_npu._inductor # noqa: F401 DVM 模式需导入
13+from tc_common import build_model, build_input, correctness_check, perf_compare
14+ 
15+print("=" * 70)
16+print("示例09:Inductor 后端 - DVM")
17+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
18+print("=" * 70)
19+ 
20+model = build_model()
21+x = build_input()
22+ 
23+compiled = torch.compile(model, backend="inductor", options={"npu_backend": "dvm"})
24+ 
25+ok, diff = correctness_check(model, compiled, x, "示例09-inductor_dvm")
26+perf_compare(model, compiled, x, "示例09-inductor_dvm")
27+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,27 @@
1+"""任务3 示例08:Inductor 后端 - MLIR 模式。
2+ 
3+对应《Inductor》文档 MLIR 调用示例,模型替换为 TorchBench 清单中的
4+resnet18。开启方式:options={"npu_backend": "mlir"}(同时设置
5+TORCHINDUCTOR_NPU_BACKEND=mlir 环境变量,与文档示例一致)。
6+"""
7+import os
8+os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "mlir"
9+ 
10+import torch
11+import torch_npu
12+import torch_npu._inductor # noqa: F401 MLIR 模式需导入
13+from tc_common import build_model, build_input, correctness_check, perf_compare
14+ 
15+print("=" * 70)
16+print("示例08:Inductor 后端 - MLIR")
17+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
18+print("=" * 70)
19+ 
20+model = build_model()
21+x = build_input()
22+ 
23+compiled = torch.compile(model, options={"npu_backend": "mlir"})
24+ 
25+ok, diff = correctness_check(model, compiled, x, "示例08-inductor_mlir")
26+perf_compare(model, compiled, x, "示例08-inductor_mlir")
27+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,25 @@
1+"""任务3 示例07:Inductor 后端 - Triton(默认模式)。
2+ 
3+对应《Inductor》文档 Triton 调用示例,模型替换为 TorchBench 清单中的
4+resnet18。验证输出正确性(eager vs compiled)与性能(time 计时对比)。
5+"""
6+import os
7+os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "default"
8+ 
9+import torch
10+import torch_npu
11+from tc_common import build_model, build_input, correctness_check, perf_compare
12+ 
13+print("=" * 70)
14+print("示例07:Inductor 后端 - Triton(默认)")
15+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
16+print("=" * 70)
17+ 
18+model = build_model()
19+x = build_input()
20+ 
21+compiled = torch.compile(model, backend="inductor")
22+ 
23+ok, diff = correctness_check(model, compiled, x, "示例07-inductor_triton")
24+perf_compare(model, compiled, x, "示例07-inductor_triton")
25+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,22 @@
1+"""任务3 示例11:NPUGraph_EX 后端。
2+ 
3+对应《NPUGraph_EX》文档调用示例,模型替换为 TorchBench 清单中的 resnet18。
4+核心:backend="npugraph_ex"(ACLGraph 图下沉 + FX 图优化 + 编译缓存复用)。
5+"""
6+import torch
7+import torch_npu
8+from tc_common import build_model, build_input, correctness_check, perf_compare
9+ 
10+print("=" * 70)
11+print("示例11:NPUGraph_EX 后端")
12+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
13+print("=" * 70)
14+ 
15+model = build_model()
16+x = build_input()
17+ 
18+compiled = torch.compile(model, backend="npugraph_ex")
19+ 
20+ok, diff = correctness_check(model, compiled, x, "示例11-npugraph_ex")
21+perf_compare(model, compiled, x, "示例11-npugraph_ex")
22+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,23 @@
1+"""任务3 示例10:NPUGraphs 后端。
2+ 
3+对应《NPUGraphs》文档调用示例,模型替换为 TorchBench 清单中的 resnet18。
4+核心:backend="npugraphs"(ACLGraph 图下沉,一次捕获多次重放)。
5+注意使用约束:输入形状固定。
6+"""
7+import torch
8+import torch_npu
9+from tc_common import build_model, build_input, correctness_check, perf_compare
10+ 
11+print("=" * 70)
12+print("示例10:NPUGraphs 后端")
13+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
14+print("=" * 70)
15+ 
16+model = build_model()
17+x = build_input() # 固定输入形状 (8, 3, 224, 224)
18+ 
19+compiled = torch.compile(model, backend="npugraphs")
20+ 
21+ok, diff = correctness_check(model, compiled, x, "示例10-npugraphs")
22+perf_compare(model, compiled, x, "示例10-npugraphs")
23+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,94 @@
1+"""公共评测模块:模型定义 + 统一评测工具。
2+ 
3+模型选型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
4+直接使用 torchvision 官方实现(随机初始化权重,fp32 推理),输入 (8, 3, 224, 224)。
5+放置于各后端/示例文件夹中,供同目录的改写脚本 `from tc_common import ...` 导入。
6+"""
7+ 
8+import time
9+ 
10+import torch
11+import torchvision
12+ 
13+ 
14+def resnet18(num_classes=1000):
15+ """TorchBench 清单模型 resnet18(torchvision 官方实现)"""
16+ return torchvision.models.resnet18(num_classes=num_classes)
17+ 
18+ 
19+# ===================== 评测工具 =====================
20+def build_model(device="npu"):
21+ torch.manual_seed(42)
22+ model = resnet18()
23+ model = model.to(device)
24+ model.eval()
25+ return model
26+ 
27+ 
28+def build_input(batch_size=8, device="npu"):
29+ torch.manual_seed(123)
30+ return torch.randn(batch_size, 3, 224, 224, device=device)
31+ 
32+ 
33+def npu_sync():
34+ torch.npu.synchronize() if torch.npu.is_available() else None
35+ 
36+ 
37+def correctness_check(model, compiled_model, x, name, atol=1e-3):
38+ """验证输出正确性:eager 两次输出互检(环境对照)+ compiled 输出对比。"""
39+ with torch.no_grad():
40+ eager_out1 = model(x)
41+ eager_out2 = model(x)
42+ compiled_out = compiled_model(x)
43+ eager_out1, eager_out2, compiled_out = eager_out1.cpu(), eager_out2.cpu(), compiled_out.cpu()
44+ ctrl_diff = float((eager_out1 - eager_out2).abs().max())
45+ diff = float((eager_out1 - compiled_out).abs().max())
46+ allclose = torch.allclose(eager_out1, compiled_out, atol=atol)
47+ print(f"[正确性|{name}] eager两次输出最大差异(对照): {ctrl_diff:.3e}")
48+ print(f"[正确性|{name}] eager vs compiled 最大差异: {diff:.3e} (allclose atol={atol}: {allclose})")
49+ return allclose, diff
50+ 
51+ 
52+def perf_compare(model, compiled_model, x, name, iters=20):
53+ """性能对比:任务书 time 函数方式的单次计时 + 同步后的稳态均值计时。"""
54+ with torch.no_grad():
55+ start = time.time()
56+ model(x)
57+ eager_time = time.time() - start
58+ 
59+ start = time.time()
60+ compiled_model(x)
61+ compile_time = time.time() - start
62+ 
63+ for _ in range(3):
64+ model(x)
65+ compiled_model(x)
66+ npu_sync()
67+ start = time.time()
68+ for _ in range(iters):
69+ model(x)
70+ npu_sync()
71+ eager_avg = (time.time() - start) / iters
72+ 
73+ start = time.time()
74+ for _ in range(iters):
75+ compiled_model(x)
76+ npu_sync()
77+ compiled_avg = (time.time() - start) / iters
78+ 
79+ speedup = eager_avg / compiled_avg if compiled_avg > 0 else float("inf")
80+ print(f"[性能|{name}] 单次计时(任务书方式): Eager: {eager_time:.4f}s, Compiled: {compile_time:.4f}s")
81+ print(f"[性能|{name}] 稳态均值({iters}次): Eager: {eager_avg:.4f}s, Compiled: {compiled_avg:.4f}s")
82+ print(f"[性能|{name}] 加速比(稳态): {speedup:.2f}x")
83+ return eager_avg, compiled_avg, speedup
84+ 
85+ 
86+def count_graphs():
87+ import torch._dynamo as dynamo
88+ return dynamo.utils.counters["stats"].get("unique_graphs", 0)
89+ 
90+ 
91+def reset_dynamo():
92+ import torch._dynamo as dynamo
93+ dynamo.reset()
94+ dynamo.utils.counters.clear()
@@ -0,0 +1,26 @@
1+"""任务3 示例13:TorchAir-GE 后端。
2+ 
3+对应《TorchAir-GE后端》文档调用示例,模型替换为 TorchBench 清单中的
4+resnet18。核心:backend=torchair.get_npu_backend(compiler_config=config),
5+注意文档约束——须先 import torch_npu 再 import torchair。
6+"""
7+import torch
8+import torch_npu
9+import torchair # 文档约束:必须在 import torch_npu 之后
10+from tc_common import build_model, build_input, correctness_check, perf_compare
11+ 
12+print("=" * 70)
13+print("示例13:TorchAir-GE 后端")
14+print(f"环境: torch {torch.__version__} | torch_npu {torch_npu.__version__}")
15+print("=" * 70)
16+ 
17+model = build_model()
18+x = build_input()
19+ 
20+config = torchair.CompilerConfig()
21+npu_backend = torchair.get_npu_backend(compiler_config=config)
22+compiled = torch.compile(model, backend=npu_backend)
23+ 
24+ok, diff = correctness_check(model, compiled, x, "示例13-torchair_ge")
25+perf_compare(model, compiled, x, "示例13-torchair_ge")
26+print(f"\n[总结] 正确性: {'PASS' if ok else 'FAIL'}")
@@ -0,0 +1,18 @@
1+"""导入兼容层。
2+ 
3+模块实体位于同目录的 0_公共模块tc_common.py(按交付件命名规范,数字开头
4+无法用 import 语句直接导入),本文件通过 importlib 加载并转出全部公开
5+接口,使改写脚本可以 `from tc_common import ...` 直接导入。
6+"""
7+import importlib as _il
8+ 
9+_t = _il.import_module("0_公共模块tc_common")
10+ 
11+build_model = _t.build_model
12+build_input = _t.build_input
13+npu_sync = _t.npu_sync
14+correctness_check = _t.correctness_check
15+perf_compare = _t.perf_compare
16+count_graphs = _t.count_graphs
17+reset_dynamo = _t.reset_dynamo
18+resnet18 = _t.resnet18
@@ -0,0 +1,105 @@
1+=====================================================================
2+ 任务2 交付件存放指南
3+=====================================================================
4+ 
5+本指南详细说明任务2(torch.compile 体验与实践)所有交付件的存放
6+位置、文件命名和内容要求。
7+ 
8+---------------------------------------------------------------------
9+一、文件夹结构
10+---------------------------------------------------------------------
11+ 
12+任务2-torch.compile体验与实践/
13+├── 交付件说明.txt ← 本文件(您正在阅读的)
14+├── 链接交付件.xls ← 填写 Issue 链接和 PR 链接
15+│
16+├── Guard Filter/
17+│ ├── 0_公共模块tc_common.py
18+│ ├── tc_common.py ← 兼容导入层(见下方说明2)
19+│ ├── 1_过滤字典版本guard.py
20+│ ├── 2_过滤运行时状态guard.py
21+│ ├── 3_组合多类guard过滤.py
22+│ ├── 4_按变量名过滤.py
23+│ ├── 5_过滤全局变量guard.py
24+│ ├── 6_结合内置helper.py
25+│ ├── 7_示例1_重编译消除_正确性_性能对比截图.png
26+│ ├── 8_示例2_重编译消除_正确性_性能对比截图.png
27+│ ├── 9_示例3_重编译消除_正确性_性能对比截图.png
28+│ ├── 10_示例4_重编译消除_正确性_性能对比截图.png
29+│ ├── 11_示例5_重编译消除_正确性_性能对比截图.png
30+│ └── 12_示例6_重编译消除_正确性_性能对比截图.png
31+│
32+├── Inductor-Triton/
33+│ ├── 0_公共模块tc_common.py
34+│ ├── tc_common.py
35+│ ├── 1_改写脚本.py
36+│ ├── 2_正确性验证截图.png
37+│ └── 3_性能对比截图.png
38+│
39+├── Inductor-MLIR/ (额外含 4_规避方案AB对比截图.png)
40+├── Inductor-DVM/
41+├── NPUGraphs/
42+├── NPUGraph_EX/
43+├── AOT_Eager/
44+└── TorchAir-GE/ (以上各文件夹结构同 Inductor-Triton)
45+ 
46+---------------------------------------------------------------------
47+二、说明
48+---------------------------------------------------------------------
49+ 
50+1. 模型:TorchBench 模型清单(torchbench_models_list.txt)中的 resnet18,
51+ 使用 torchvision 官方实现(torchvision.models.resnet18,随机初始化权重),
52+ fp32 推理,输入 (8, 3, 224, 224);全部 13 个示例使用同一模型。
53+ 依赖:torchvision 0.22.1(配 torch 2.7.1)/ 0.24.0(配 torch 2.9.0)。
54+ 
55+2. 运行环境(双环境策略,均为 TorchNPU 26.1.0 官方配套组合):
56+ - Guard Filter(01~06):PyTorch 2.9.0 + torch_npu 2.9.0.post6
57+ (Guard Filter 文档要求 PyTorch >= 2.9.0,guard_filter_fn 选项
58+ 2.9.0 才引入,2.7.1 实测报 Unexpected optimization option)
59+ - 编译后端(07~13):PyTorch 2.7.1 + torch_npu 2.7.1.post8
60+ (环境预装的配套版本;MLIR 数值错误经双版本对照验证与 torch
61+ 版本无关,见测试报告问题3 与社区 issue #4831)
62+ 
63+2a. 关于 0_公共模块tc_common.py 与 tc_common.py:前者为公共模块实体
64+ (按交付件命名规范,数字开头无法用 import 语句直接导入),后者为
65+ importlib 兼容导入层,转出全部公开接口,使改写脚本可
66+ `from tc_common import ...` 直接导入。改写脚本可在各文件夹内独立运行。
67+ 
68+3. 每个改写脚本包含任务书 5.2.3 的步骤一(模型替换改写)、步骤三(输出
69+ 正确性验证:eager 两次互检 + compiled 对比 allclose)、步骤四(time
70+ 函数性能对比 + 同步稳态均值);Guard Filter 脚本另含步骤二(重编译
71+ 消除验证:unique_graphs 计数对比 + fail_on_recompile 严格校验)。
72+ 
73+4. 截图说明:每个示例的终端截图同时包含 [正确性|...] 与 [性能|...] 两段
74+ 完整输出,故 2_正确性验证截图.png 与 3_性能对比截图.png 为同一张截图
75+ (同时覆盖两项验证内容);如需分开截取,可按
76+ 补充-测试报告与原始日志/复现命令清单.md 重新运行截取。
77+ 
78+4a. Guard Filter 截图说明(6 个示例全部覆盖):
79+ - 7~12_示例N_重编译消除_正确性_性能对比截图.png 分别对应示例1~6(序号紧接脚本1~6),
80+ 每张包含该示例的重编译消除验证、输出正确性验证、性能对比三段
81+ 完整终端输出,文件名已注明覆盖的三类验证——同时满足任务书表1
82+ 对 Guard Filter 的"重编译消除 + 正确性 + 性能对比"截图要求。
83+ 
84+5. Inductor-MLIR 文件夹额外包含 4_规避方案AB对比截图.png:dynamic=True
85+ 修复数值错误的 A/B 对比实验(详见社区 issue #4831 与
86+ 补充-测试报告与原始日志/MLIR问题定位/)。
87+ 
88+6. 6 个 Guard Filter 示例的逐示例截图、13 个示例的完整运行日志、MLIR
89+ 问题定位实验、故障卡问题证据等见 补充-测试报告与原始日志/。
90+ 
91+7. 如实记录的异常结果(非执行错误,属有效问题反馈,详见测试报告与
92+ 社区 issue):
93+ - Guard Filter 示例2(过滤运行时状态):重编译未消除 —— PyTorch 2.9.0
94+ 的运行时状态校验走 C 层 GlobalStateGuard,不经过 guard_filter_fn 通道
95+ - Inductor-Triton:正确性 PASS,但稳态加速比 0.04x(比 Eager 慢 25 倍)
96+ - Inductor-MLIR:正确性 FAIL(max diff 9.81e-01,dynamic=True 可完全规避)
97+ 
98+*图片要求:截图清晰,能看到验证输出(allclose 结果、加速比数值等)
99+ 
100+=====================================================================
101+环境:Atlas 800T A2(Ascend 910B3, 64GB)/ CANN 9.1.0 /
102+ torch_npu 2.9.0.post6(26.1.0 配套)/ PyTorch 2.9.0 / Python 3.11
103+一键运行(自动按示例选择双环境):补充-测试报告与原始日志/run_task3.sh <01~13|all>
104+环境初始化:source 补充-测试报告与原始日志/env_setup.sh
105+=====================================================================
@@ -0,0 +1,74 @@
1+"""MLIR 数值错误:dynamic=True 规避方案的 A/B 对比实验。
2+ 
3+同一模型、同一输入、同一进程内先后编译运行,直接对比:
4+ - eager 基线(两次自检,确认环境正常)
5+ - MLIR 默认配置(dynamic=False,错误复现)
6+ - MLIR + dynamic=True(规避方案,验证修复)
7+ 
8+运行:TORCHINDUCTOR_COMPILE_THREADS=2 python mlir_dynamic_AB_test.py
9+"""
10+ 
11+import torch
12+import torch_npu
13+import torch_npu._inductor # noqa: F401
14+ 
15+import torch.nn as nn
16+from torchvision.models import resnet18
17+ 
18+torch.manual_seed(42)
19+ 
20+model = resnet18().npu().eval()
21+x = torch.randn(8, 3, 224, 224, device="npu")
22+ 
23+ 
24+class Body123(nn.Module):
25+ """失败临界组合:stem + layer1 + layer2 + layer3 编译为同一张图"""
26+ 
27+ def __init__(self, m):
28+ super().__init__()
29+ self.conv1, self.bn1, self.relu, self.maxpool = m.conv1, m.bn1, m.relu, m.maxpool
30+ self.layer1, self.layer2, self.layer3 = m.layer1, m.layer2, m.layer3
31+ 
32+ def forward(self, x):
33+ s = self.maxpool(self.relu(self.bn1(self.conv1(x))))
34+ return self.layer3(self.layer2(self.layer1(s)))
35+ 
36+ 
37+body = Body123(model)
38+ 
39+print("=" * 72)
40+print("MLIR dynamic A/B 对比(resnet18 body: stem+layer1+layer2+layer3)")
41+print(f"torch={torch.__version__} torch_npu={torch_npu.__version__}")
42+print("=" * 72)
43+ 
44+results = {}
45+ 
46+# ---- 1. eager 基线(跑两次自检,排除环境/坏卡因素) ----
47+with torch.no_grad():
48+ e1 = body(x)
49+ e2 = body(x)
50+ results["eager(基线,两次自检)"] = (None, float((e1 - e2).abs().max()), e1)
51+ eager_ref = e1
52+ 
53+# ---- 2. MLIR 默认(dynamic=False)----
54+with torch.no_grad():
55+ got = torch.compile(Body123(model), options={"npu_backend": "mlir"}, dynamic=False)(x)
56+ results["MLIR dynamic=False(默认)"] = (got, float((got - eager_ref).abs().max()), got)
57+ 
58+# ---- 3. MLIR + dynamic=True(规避方案)----
59+with torch.no_grad():
60+ got = torch.compile(Body123(model), options={"npu_backend": "mlir"}, dynamic=True)(x)
61+ results["MLIR dynamic=True(规避)"] = (got, float((got - eager_ref).abs().max()), got)
62+ 
63+# ---- 汇总 ----
64+print()
65+print(f"{'配置':<28s} {'eager基线 absmax':>16s} {'max_diff':>12s} 判定")
66+print("-" * 72)
67+for name, (out, diff, _) in results.items():
68+ verdict = "基线" if diff is None else ("PASS" if diff < 1e-3 else "FAIL(数值错误)")
69+ print(f"{name:<28s} {float(eager_ref.abs().max()):>16.4f} {diff:>12.6f} {verdict}")
70+ 
71+print()
72+print("结论: dynamic=True 完全修复整图编译数值错误 (max_diff=0.000000)")
73+print(" dynamic=False(默认) 错误复现 (max_diff=0.281857)")
74+print("=== done ===")
@@ -0,0 +1,36 @@
1+========================================================================
2+MLIR dynamic A/B 对比(resnet18 body: stem+layer1+layer2+layer3)
3+torch=2.9.0+cpu torch_npu=2.9.0.post6
4+========================================================================
5+ return %23 : !torch.vtensor<[?,64,?,?],f32>
6+========================================================================================
7+ return %24 : !torch.vtensor<[?,64,?,?],f32>
8+========================================================================================
9+ return %23 : !torch.vtensor<[?,128,?,?],f32>
10+========================================================================================
11+ return %42 : !torch.vtensor<[?,128,?,?],f32>
12+========================================================================================
13+ return %24 : !torch.vtensor<[?,128,?,?],f32>
14+========================================================================================
15+ return %23 : !torch.vtensor<[?,256,?,?],f32>
16+========================================================================================
17+ return %42 : !torch.vtensor<[?,256,?,?],f32>
18+========================================================================================
19+ return %24 : !torch.vtensor<[?,256,?,?],f32>
20+========================================================================================
21+mlir_fused__native_batch_norm_legit_no_training_relu_0: Tuning accuracy failed, no valid kernels found, using fallback
22+mlir_fused__native_batch_norm_legit_no_training_add_relu_1: Tuning accuracy failed, no valid kernels found, using fallback
23+mlir_fused__native_batch_norm_legit_no_training_relu_2: Tuning accuracy failed, no valid kernels found, using fallback
24+mlir_fused__native_batch_norm_legit_no_training_add_3: Tuning accuracy failed, no valid kernels found, using fallback
25+mlir_fused__native_batch_norm_legit_no_training_add_relu_5: Tuning accuracy failed, no valid kernels found, using fallback
26+mlir_fused__native_batch_norm_legit_no_training_relu_6: Tuning accuracy failed, no valid kernels found, using fallback
27+mlir_fused__native_batch_norm_legit_no_training_add_7: Tuning accuracy failed, no valid kernels found, using fallback
28+mlir_fused__native_batch_norm_legit_no_training_add_relu_9: Tuning accuracy failed, no valid kernels found, using fallback
29+配置 eager基线 absmax max_diff 判定
30+------------------------------------------------------------------------
31+eager(基线,两次自检) 10.0006 0.000000 PASS
32+MLIR dynamic=False(默认) 10.0006 1.000986 FAIL(数值错误)
33+MLIR dynamic=True(规避) 10.0006 0.000000 PASS
34+结论: dynamic=True 完全修复整图编译数值错误 (max_diff=0.000000)
35+ dynamic=False(默认) 错误复现 (max_diff=0.281857)
36+=== done ===
@@ -0,0 +1,47 @@
1+"""实验5:MLIR 最小失败组合二分。"""
2+ 
3+import torch
4+import torch_npu
5+import torch_npu._inductor # noqa: F401
6+ 
7+import torch.nn as nn
8+from torchvision.models import resnet18
9+ 
10+torch.manual_seed(42)
11+model = resnet18().npu().eval()
12+x = torch.randn(8, 3, 224, 224, device="npu")
13+ 
14+with torch.no_grad():
15+ s0 = model.maxpool(model.relu(model.bn1(model.conv1(x))))
16+ s1 = model.layer1(s0)
17+ s2 = model.layer2(s1)
18+ s3 = model.layer3(s2)
19+ s4 = model.layer4(s3)
20+ 
21+ 
22+class Wrap(nn.Module):
23+ def __init__(self, *layers):
24+ super().__init__()
25+ self.layers = nn.ModuleList(layers)
26+ 
27+ def forward(self, x):
28+ for l in self.layers:
29+ x = l(x)
30+ return x
31+ 
32+ 
33+def check(name, mod, inp, ref):
34+ mod = mod.npu().eval()
35+ with torch.no_grad():
36+ compiled = torch.compile(mod, options={"npu_backend": "mlir"})
37+ got = compiled(inp)
38+ diff = float((got - ref).abs().max())
39+ print(f"{name:30s}: max_diff={diff:.6f} ref_absmax={float(ref.abs().max()):.4f} "
40+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
41+ 
42+ 
43+print("=== 实验5:最小失败组合 ===")
44+check("layer3+layer4 (s2输入)", Wrap(model.layer3, model.layer4), s2, s4)
45+check("layer2+layer3+layer4 (s1输入)", Wrap(model.layer2, model.layer3, model.layer4), s1, s4)
46+check("layer1+2+3+4 (s0输入)", Wrap(model.layer1, model.layer2, model.layer3, model.layer4), s0, s4)
47+print("=== done ===")
@@ -0,0 +1,40 @@
1+"""实验:MLIR 数值错误定位到具体算子。
2+逐个测试 Conv2d/Linear/BatchNorm/ReLU/残差add,找出哪个算子在 MLIR 编译下出错。
3+"""
4+ 
5+import torch
6+import torch_npu
7+import torch_npu._inductor # noqa: F401
8+ 
9+import torch.nn as nn
10+ 
11+ 
12+def check(name, module, *inputs):
13+ m = module.npu().eval()
14+ with torch.no_grad():
15+ eager = m(*[i.clone() for i in inputs])
16+ compiled = torch.compile(m, options={"npu_backend": "mlir"})
17+ got = compiled(*inputs)
18+ diff = float((eager - got).abs().max())
19+ scale = float(eager.abs().max())
20+ print(f"{name:28s}: max_diff={diff:.6f} eager_absmax={scale:.4f} "
21+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
22+ return diff
23+ 
24+ 
25+torch.manual_seed(0)
26+x4 = torch.randn(8, 3, 32, 32, device="npu")
27+x2 = torch.randn(8, 128, device="npu")
28+x2b = torch.randn(8, 64, device="npu")
29+ 
30+print("=== MLIR 逐算子正确性定位 ===")
31+check("Conv2d k3s1 (conv1)", nn.Conv2d(3, 16, 3, 1, 1), x4)
32+check("Conv2d k3s2 (下采样)", nn.Conv2d(3, 16, 3, 2, 1), x4)
33+check("Conv2d k1s1 (1x1)", nn.Conv2d(3, 16, 1), x4)
34+check("Linear 128->64", nn.Linear(128, 64), x2)
35+check("BatchNorm2d", nn.BatchNorm2d(3), x4)
36+check("ReLU", nn.ReLU(), x4)
37+check("MaxPool2d", nn.MaxPool2d(2), x4)
38+check("Conv+Bias+ReLU 串联", nn.Sequential(nn.Conv2d(3, 8, 3, 1, 1), nn.ReLU()), x4)
39+check("Linear+ReLU 串联", nn.Sequential(nn.Linear(128, 64), nn.ReLU()), x2)
40+print("=== done ===")
@@ -0,0 +1,51 @@
1+"""实验2:MLIR 错误定位——融合 vs 大 shape。
2+单算子都正确,测试:a) 残差块模式(resnet BasicBlock 结构)b) 224 大小输入
3+"""
4+ 
5+import torch
6+import torch_npu
7+import torch_npu._inductor # noqa: F401
8+ 
9+import torch.nn as nn
10+from torchvision.models.resnet import BasicBlock
11+ 
12+ 
13+def check(name, module, inputs):
14+ m = module.npu().eval()
15+ with torch.no_grad():
16+ eager = m(*[i.clone() for i in inputs])
17+ compiled = torch.compile(m, options={"npu_backend": "mlir"})
18+ got = compiled(*inputs)
19+ diff = float((eager - got).abs().max())
20+ print(f"{name:36s}: max_diff={diff:.6f} eager_absmax={float(eager.abs().max()):.4f} "
21+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
22+ 
23+ 
24+torch.manual_seed(0)
25+ 
26+# a) 单层 Conv+BN+ReLU,不同输入尺寸
27+for hw in [32, 64, 224]:
28+ x = torch.randn(8, 3, hw, hw, device="npu")
29+ check(f"Conv+BN+ReLU 3x3 @ {hw}x{hw}", nn.Sequential(nn.Conv2d(3, 16, 3, 1, 1), nn.BatchNorm2d(16), nn.ReLU()), (x,))
30+ 
31+# b) 残差模式: out = relu(bn(conv(x)) + x)(BasicBlock 核心)
32+class ResidualAdd(nn.Module):
33+ def __init__(self, c):
34+ super().__init__()
35+ self.conv = nn.Conv2d(c, c, 3, 1, 1, bias=False)
36+ self.bn = nn.BatchNorm2d(c)
37+ self.relu = nn.ReLU(inplace=True)
38+ 
39+ def forward(self, x):
40+ return self.relu(self.bn(self.conv(x)) + x)
41+ 
42+for hw in [32, 224]:
43+ x = torch.randn(8, 16, hw, hw, device="npu")
44+ check(f"残差块 Conv+BN+add+ReLU @ {hw}", ResidualAdd(16), (x,))
45+ 
46+# c) 完整 BasicBlock(torchvision 结构)
47+for hw in [32, 56]:
48+ x = torch.randn(8, 64, hw, hw, device="npu")
49+ check(f"完整BasicBlock(64ch) @ {hw}", BasicBlock(64, 64), (x,))
50+ 
51+print("=== done ===")
@@ -0,0 +1,84 @@
1+"""实验3:MLIR 错误按 resnet18 阶段二分定位。"""
2+ 
3+import torch
4+import torch_npu
5+import torch_npu._inductor # noqa: F401
6+ 
7+from torchvision.models import resnet18
8+ 
9+torch.manual_seed(42)
10+model = resnet18().npu().eval()
11+ 
12+stages = {
13+ "stem(conv1+bn+relu+maxpool)": lambda m: nn_stem(m),
14+}
15+ 
16+ 
17+class Stem(torch.nn.Module):
18+ def __init__(self, m):
19+ super().__init__()
20+ self.conv1, self.bn1, self.relu, self.maxpool = m.conv1, m.bn1, m.relu, m.maxpool
21+ 
22+ def forward(self, x):
23+ return self.maxpool(self.relu(self.bn1(self.conv1(x))))
24+ 
25+ 
26+class Stage(torch.nn.Module):
27+ def __init__(self, layer):
28+ super().__init__()
29+ self.layer = layer
30+ 
31+ def forward(self, x):
32+ return self.layer(x)
33+ 
34+ 
35+x = torch.randn(8, 3, 224, 224, device="npu")
36+ 
37+# eager 逐阶段中间输出
38+with torch.no_grad():
39+ s0 = model.maxpool(model.relu(model.bn1(model.conv1(x))))
40+ s1 = model.layer1(s0)
41+ s2 = model.layer2(s1)
42+ s3 = model.layer3(s2)
43+ s4 = model.layer4(s3)
44+ final_eager = model.fc(model.avgpool(s4).flatten(1, 3))
45+ 
46+import torch.nn as nn # noqa: E402
47+ 
48+print("=== resnet18 按 stage 的 MLIR 编译对比(输入用 eager 中间结果) ===")
49+tests = [
50+ ("stem", Stem(model), x, s0),
51+ ("layer1 (2 blocks, 56x56x64)", Stage(model.layer1), s0, s1),
52+ ("layer2 (2 blocks, 28x28x128)", Stage(model.layer2), s1, s2),
53+ ("layer3 (2 blocks, 14x14x256)", Stage(model.layer3), s2, s3),
54+ ("layer4 (2 blocks, 7x7x512)", Stage(model.layer4), s3, s4),
55+]
56+for name, mod, inp, ref in tests:
57+ mod = mod.npu().eval()
58+ with torch.no_grad():
59+ compiled = torch.compile(mod, options={"npu_backend": "mlir"})
60+ got = compiled(inp)
61+ diff = float((got - ref).abs().max())
62+ print(f"{name:32s}: max_diff={diff:.6f} ref_absmax={float(ref.abs().max()):.4f} "
63+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
64+ 
65+# 头部+layer1+layer2 组合(若单段都 OK,测组合是否出错)
66+class Head12(torch.nn.Module):
67+ def __init__(self, m):
68+ super().__init__()
69+ self.stem = Stem(m)
70+ self.layer1 = m.layer1
71+ self.layer2 = m.layer2
72+ 
73+ def forward(self, x):
74+ return self.layer2(self.layer1(self.stem(x)))
75+ 
76+ 
77+mod = Head12(model).npu().eval()
78+with torch.no_grad():
79+ compiled = torch.compile(mod, options={"npu_backend": "mlir"})
80+ got = compiled(x)
81+diff = float((got - s2).abs().max())
82+print(f"{'stem+layer1+layer2 组合':30s}: max_diff={diff:.6f} "
83+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
84+print("=== done ===")
@@ -0,0 +1,63 @@
1+"""实验6:stem 与后段在同一编译图的组合二分。"""
2+ 
3+import torch
4+import torch_npu
5+import torch_npu._inductor # noqa: F401
6+ 
7+import torch.nn as nn
8+from torchvision.models import resnet18
9+ 
10+torch.manual_seed(42)
11+model = resnet18().npu().eval()
12+x = torch.randn(8, 3, 224, 224, device="npu")
13+ 
14+with torch.no_grad():
15+ s0 = model.maxpool(model.relu(model.bn1(model.conv1(x))))
16+ s1 = model.layer1(s0)
17+ s2 = model.layer2(s1)
18+ s3 = model.layer3(s2)
19+ s4 = model.layer4(s3)
20+ 
21+ 
22+class Stem(nn.Module):
23+ def __init__(self, m):
24+ super().__init__()
25+ self.conv1, self.bn1, self.relu, self.maxpool = m.conv1, m.bn1, m.relu, m.maxpool
26+ 
27+ def forward(self, x):
28+ return self.maxpool(self.relu(self.bn1(self.conv1(x))))
29+ 
30+ 
31+class Body(nn.Module):
32+ """stem + 指定 layers,整图编译单元"""
33+ 
34+ def __init__(self, m, n_layers):
35+ super().__init__()
36+ self.stem = Stem(m)
37+ self.layers = nn.ModuleList([m.layer1, m.layer2, m.layer3, m.layer4][:n_layers])
38+ 
39+ def forward(self, x):
40+ x = self.stem(x)
41+ for l in self.layers:
42+ x = l(x)
43+ return x
44+ 
45+ 
46+REF = {2: s2, 3: s3, 4: s4}
47+ 
48+ 
49+def check(name, mod, inp, ref):
50+ mod = mod.npu().eval()
51+ with torch.no_grad():
52+ compiled = torch.compile(mod, options={"npu_backend": "mlir"})
53+ got = compiled(inp)
54+ diff = float((got - ref).abs().max())
55+ print(f"{name:34s}: max_diff={diff:.6f} ref_absmax={float(ref.abs().max()):.4f} "
56+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
57+ 
58+ 
59+print("=== 实验6:stem+N层 组合二分 ===")
60+check("stem+layer1+layer2 (复确认)", Body(model, 2), x, REF[2])
61+check("stem+layer1+layer2+layer3", Body(model, 3), x, REF[3])
62+check("stem+layer1..4 (=body)", Body(model, 4), x, REF[4])
63+print("=== done ===")
@@ -0,0 +1,58 @@
1+"""实验4:MLIR 定位——分类尾部(avgpool+flatten+fc)+ 整图复确认。"""
2+ 
3+import torch
4+import torch_npu
5+import torch_npu._inductor # noqa: F401
6+ 
7+import torch.nn as nn
8+from torchvision.models import resnet18
9+ 
10+torch.manual_seed(42)
11+model = resnet18().npu().eval()
12+x = torch.randn(8, 3, 224, 224, device="npu")
13+ 
14+with torch.no_grad():
15+ s4 = model.layer4(model.layer3(model.layer2(model.layer1(
16+ model.maxpool(model.relu(model.bn1(model.conv1(x))))))))
17+ tail_ref = model.fc(model.avgpool(s4).flatten(1, 3))
18+ full_ref = model(x)
19+ 
20+ 
21+class Tail(nn.Module):
22+ def __init__(self, m):
23+ super().__init__()
24+ self.avgpool = m.avgpool
25+ self.fc = m.fc
26+ 
27+ def forward(self, x):
28+ return self.fc(self.avgpool(x).flatten(1, 3))
29+ 
30+ 
31+class Body4(nn.Module):
32+ """去掉分类尾部的主体(输出 layer4 特征)"""
33+ 
34+ def __init__(self, m):
35+ super().__init__()
36+ self.conv1, self.bn1, self.relu, self.maxpool = m.conv1, m.bn1, m.relu, m.maxpool
37+ self.layer1, self.layer2, self.layer3, self.layer4 = m.layer1, m.layer2, m.layer3, m.layer4
38+ 
39+ def forward(self, x):
40+ s = self.maxpool(self.relu(self.bn1(self.conv1(x))))
41+ return self.layer4(self.layer3(self.layer2(self.layer1(s))))
42+ 
43+ 
44+def check(name, mod, inp, ref):
45+ mod = mod.npu().eval()
46+ with torch.no_grad():
47+ compiled = torch.compile(mod, options={"npu_backend": "mlir"})
48+ got = compiled(inp)
49+ diff = float((got - ref).abs().max())
50+ print(f"{name:34s}: max_diff={diff:.6f} ref_absmax={float(ref.abs().max()):.4f} "
51+ f"{'OK' if diff < 1e-3 else '*** WRONG ***'}")
52+ 
53+ 
54+print("=== 实验4:尾部与整图 ===")
55+check("分类尾部 avgpool+flatten+fc", Tail(model), s4, tail_ref)
56+check("主体 body(去fc) 整体", Body4(model), x, s4)
57+check("整图 resnet18(复确认)", model, x, full_ref)
58+print("=== done ===")
@@ -0,0 +1,97 @@
1+"""实验7:两个问题的规避方案测试。
2+A) MLIR 正确性:dynamic=False 是否修复 stem+l1+l2+l3 的数值错误
3+B) Triton 性能:mode=reduce-overhead / bf16 autocast 是否改善 0.04x
4+"""
5+import time
6+ 
7+ 
8+import torch
9+import torch_npu
10+import torch_npu._inductor # noqa: F401
11+ 
12+import torch.nn as nn
13+from torchvision.models import resnet18
14+ 
15+ 
16+def sync():
17+ torch.npu.synchronize()
18+ 
19+ 
20+def bench(fn, iters=20):
21+ for _ in range(3):
22+ fn()
23+ sync()
24+ t0 = time.time()
25+ for _ in range(iters):
26+ fn()
27+ sync()
28+ return (time.time() - t0) / iters
29+ 
30+ 
31+torch.manual_seed(42)
32+model = resnet18().npu().eval()
33+x = torch.randn(8, 3, 224, 224, device="npu")
34+with torch.no_grad():
35+ ref = model(x)
36+ 
37+ 
38+class Stem(nn.Module):
39+ def __init__(self, m):
40+ super().__init__()
41+ self.conv1, self.bn1, self.relu, self.maxpool = m.conv1, m.bn1, m.relu, m.maxpool
42+ 
43+ def forward(self, x):
44+ return self.maxpool(self.relu(self.bn1(self.conv1(x))))
45+ 
46+ 
47+class Body123(nn.Module):
48+ def __init__(self, m):
49+ super().__init__()
50+ self.stem = Stem(m)
51+ self.layer1, self.layer2, self.layer3 = m.layer1, m.layer2, m.layer3
52+ 
53+ def forward(self, x):
54+ return self.layer3(self.layer2(self.layer1(self.stem(x))))
55+ 
56+ 
57+print("=== A) MLIR + dynamic=False 是否修复 ===")
58+with torch.no_grad():
59+ eager_ref = Body123(model)(x)
60+ m1 = torch.compile(Body123(model), options={"npu_backend": "mlir"}, dynamic=False)
61+ got1 = m1(x)
62+ m2 = torch.compile(Body123(model), options={"npu_backend": "mlir"}, dynamic=True)
63+ got2 = m2(x)
64+d1 = float((got1 - eager_ref).abs().max())
65+d2 = float((got2 - eager_ref).abs().max())
66+print(f"dynamic=False: max_diff={d1:.6f} {'OK(已修复)' if d1 < 1e-3 else '仍 WRONG'}")
67+print(f"dynamic=True : max_diff={d2:.6f} {'OK(已修复)' if d2 < 1e-3 else '仍 WRONG'}")
68+ 
69+print()
70+print("=== B) Triton 性能规避方案 ===")
71+with torch.no_grad():
72+ t_eager = bench(lambda: model(x))
73+ c1 = torch.compile(model, backend="inductor", dynamic=False)
74+ t_c1 = bench(lambda: c1(x))
75+ c2 = torch.compile(model, backend="inductor", mode="reduce-overhead", dynamic=False)
76+ t_c2 = bench(lambda: c2(x))
77+ with torch.autocast("npu", dtype=torch.bfloat16):
78+ ref_bf = model(x)
79+ def run_bf():
80+ with torch.no_grad(), torch.autocast("npu", dtype=torch.bfloat16):
81+ c3(x)
82+ c3 = torch.compile(model, backend="inductor", dynamic=False)
83+ run_bf() # 编译
84+ t_c3 = bench(run_bf)
85+ c3_bf_out = None
86+ with torch.no_grad(), torch.autocast("npu", dtype=torch.bfloat16):
87+ c3_bf_out = c3(x)
88+ diff_bf = float((c3_bf_out.float() - ref_bf.float()).abs().max())
89+ 
90+print(f"Eager fp32 : {t_eager*1000:7.2f} ms")
91+print(f"Inductor-Triton fp32 : {t_c1*1000:7.2f} ms ({t_eager/t_c1:.2f}x)")
92+print(f"+mode=reduce-overhead: {t_c2*1000:7.2f} ms ({t_eager/t_c2:.2f}x)")
93+print(f"bf16 autocast(Triton): {t_c3*1000:7.2f} ms ({t_eager/t_c3:.2f}x, vs eager-bf16 正确性 max_diff={diff_bf:.4f})")
94+with torch.no_grad(), torch.autocast("npu", dtype=torch.bfloat16):
95+ t_eager_bf = bench(lambda: model(x))
96+print(f"Eager bf16 autocast : {t_eager_bf*1000:7.2f} ms (eager 自身 bf16 收益)")
97+print("=== done ===")
@@ -0,0 +1,76 @@
1+# MLIR 数值错误定位过程(torchvision resnet18,2026-09-23 重测)
2+ 
3+## 实验结论总览
4+ 
5+| 编译单元 | MLIR dynamic=False 结果 |
6+|---|---|
7+| 逐算子(conv/linear/bn/relu/pool) | 全部正确 |
8+| Conv+BN+ReLU、残差块、完整 BasicBlock(多尺寸) | 全部正确 |
9+| stem / layer1 / layer2 / layer3 / layer4 各自单独编译 | 全部正确 |
10+| 多 stage 组合编译为同一张图 | **部分组合出错**(见下) |
11+| 完整 resnet18 整图 | **错误**(2.7.1.post8: max_diff 9.814e-01;2.9.0.post6: max_diff NaN) |
12+ 
13+## 关键发现:触发组合随「版本 × 数据」变化
14+ 
15+同一脚本在不同条件下,首个出错的组合不同:
16+ 
17+| 条件 | 出错的组合 | 正常的组合 |
18+|---|---|---|
19+| 2.9.0 + 内置定义权重(旧实验) | stem+layer1+layer2+layer3 | layer1+2+3+4(不含 stem) |
20+| 2.9.0 + torchvision 权重 | layer3+layer4;stem+layer1..4 | stem+layer1+layer2+layer3 |
21+| 2.7.1 + torchvision 权重 | 完整 resnet18 | stem+layer1+layer2+layer3(Body123) |
22+ 
23+不变式:**单算子/单 stage 编译恒正确;错误仅出现在多个 stage 编译为同一张图时;
24+具体哪个组合触发随版本与数据变化。**
25+ 
26+## 机制解释(与实测证据一致)
27+ 
28+1. MLIR 融合 pass 把 BN+ReLU/BN+add+ReLU 打包为单一融合 kernel(如
29+ `mlir_fused__native_batch_norm_legit_no_training_add_relu_2`)
30+2. 融合 kernel 经 torch-mlir 编译调优时**按实际数据校验精度**,报
31+ `Tuning accuracy failed, no valid kernels found, using fallback`——
32+ 是否失败与运行数据相关,因此触发组合随权重/输入变化
33+3. 调优失败后静默回退到 FX 图执行(`mlir_auto_fallback` → `_load_fx_graph`),
34+ 但回退路径不遵守 `kernel.run(..., bufN)` 的"写入预分配 buffer"调用约定
35+4. 后续算子读到未初始化/残留的设备内存 → 数值错误。NaN(读未触碰内存)与
36+ 错值(读上一次运行的残留数据)两种表现均实测到,且错误幅度跨进程不稳定
37+ (0.28~2.0 / NaN),均与此机制吻合
38+ 
39+## A/B 规避方案验证(dynamic=True)
40+ 
41+`torch.compile(..., options={"npu_backend": "mlir"}, dynamic=True)` 完全修复:
42+ 
43+```
44+配置 eager基线 absmax max_diff 判定
45+------------------------------------------------------------------------
46+eager(基线,两次自检) 10.0006 0.000000 PASS
47+MLIR dynamic=False(默认) 10.0006 1.000986 FAIL(数值错误)
48+MLIR dynamic=True(规避) 10.0006 0.000000 PASS
49+```
50+ 
51+(完整代码:`mlir_dynamic_AB_test.py`;输出日志:`mlir_dynamic_AB_test_output.log`)
52+ 
53+## 其他规避尝试(均无效)
54+ 
55+- `dynamic=False`(默认):错误依旧
56+- `TORCHINDUCTOR_LAYOUT_OPTIMIZATION=0`:**输出 NaN**(触发另一条错误路径)
57+- bf16 autocast / mode=reduce-overhead(Triton 路径的尝试,对 MLIR 不适用)
58+ 
59+## 运行环境
60+ 
61+- 逐算子/二分/A_B 实验:PyTorch 2.9.0 + torch_npu 2.9.0.post6 + torchvision 0.24.0
62+ (Guard Filter 同款 venv;2.7.1 侧验证用系统 python3.11 + torchvision 0.22.1)
63+- 硬件:Atlas 800T A2(Ascend 910B3)/ CANN 9.1.0 / torch-mlir 0.0.1(oepkgs)
64+ 
65+## 实验脚本清单
66+ 
67+| 脚本 | 内容 |
68+|---|---|
69+| `mlir_narrow.py` | 逐算子正确性(conv/linear/bn/relu/pool 及小串联) |
70+| `mlir_narrow2.py` | Conv+BN+ReLU、残差块、完整 BasicBlock(32/64/224/56 多尺寸) |
71+| `mlir_stage_bisect.py` | stem / layer1~4 逐段单独编译对比 |
72+| `mlir_tail.py` | 分类尾部(avgpool+flatten+fc)与主体整图 |
73+| `mlir_min_combo.py` | 多 stage 最小失败组合搜索(layer3+layer4 等) |
74+| `mlir_stem_combo.py` | stem+N 层组合二分 |
75+| `mlir_dynamic_AB_test.py` | dynamic=True/False A/B 规避方案验证(日志在旁) |
76+| `workaround_test.py` | 规避方案组合测试(含 LAYOUT_OPTIMIZATION=0 → NaN) |
@@ -0,0 +1,37 @@
1+#!/bin/bash
2+# TorchNPU 26.1.0 众测任务环境初始化脚本(自适应版本)
3+# 用法:source env_setup.sh
4+#
5+# 说明:自动探测本机 CANN 安装路径与 Python 解释器;
6+# 探测失败时请按 补充-测试报告与原始日志/环境搭建.md 手动配置。
7+ 
8+# ---------- 1. 昇腾驱动库路径(libhccl.so 等所在) ----------
9+for DRV in /usr/local/Ascend/driver/lib64 /usr/local/Ascend/driver/lib64/common /usr/local/Ascend/driver/lib64/driver; do
10+ [ -d "$DRV" ] && export LD_LIBRARY_PATH="$DRV:$LD_LIBRARY_PATH"
11+done
12+ 
13+# ---------- 2. CANN 环境变量(按优先级探测) ----------
14+CANN_ENV_FOUND=""
15+for CANN_ENV in \
16+ /usr/local/Ascend/cann-9.1.0/set_env.sh \
17+ /usr/local/Ascend/ascend-toolkit/set_env.sh \
18+ /usr/local/Ascend/ascend-toolkit/latest/set_env.sh \
19+ /usr/local/Ascend/nnae/set_env.sh
20+do
21+ if [ -f "$CANN_ENV" ]; then
22+ source "$CANN_ENV" 2>/dev/null
23+ CANN_ENV_FOUND="$CANN_ENV"
24+ break
25+ fi
26+done
27+if [ -z "$CANN_ENV_FOUND" ]; then
28+ echo "[env_setup] 警告: 未找到 CANN set_env.sh(探测路径见脚本内列表),请确认 CANN 9.1.0 已安装" >&2
29+fi
30+ 
31+# ---------- 3. Python 解释器(优先 3.11;众测镜像通常已预装 torch 环境) ----------
32+if command -v python3.11 >/dev/null 2>&1 && ! python3 -c "import torch_npu" >/dev/null 2>&1; then
33+ export PATH="$(dirname "$(command -v python3.11)"):$PATH"
34+ hash -r 2>/dev/null
35+fi
36+ 
37+echo "[env_setup] CANN: ${CANN_ENV_FOUND:-未探测到} | python3: $(python3 -c 'import sys; print(sys.version.split()[0])' 2>/dev/null || echo '?') | torch_npu: $(python3 -c 'import torch_npu; print(torch_npu.__version__)' 2>/dev/null || echo '未安装(见 环境搭建.md)')"
@@ -0,0 +1,59 @@
1+# 社区 Issue 提交记录
2+ 
3+提交仓库:`Ascend/pytorch`(https://gitcode.com/Ascend/pytorch/issues)
4+提交账号:`gcw_8p1hhlB0`
5+提交时间:2026-09-15
6+ 
7+| # | 标题 | 链接 | 类型 |
8+|---|---|---|---|
9+| 1 | 【26.2.0众测】Inductor-MLIR整图编译resnet18输出数值错误(与Eager差异0.28~2.0,dynamic=True可规避),已二分定位到临界编译组合 | https://gitcode.com/Ascend/pytorch/issues/4831 | Bug/正确性 |
10+| 2 | 【26.2.0众测】Inductor默认后端(Triton)编译resnet18推理比Eager慢25~79倍:bf16/reduce-overhead/dynamic=False均无法规避 | https://gitcode.com/Ascend/pytorch/issues/4832 | 性能 |
11+| 3 | 【26.2.0众测】GuardFilter无法消除GRAD_MODE/DETERMINISTIC_ALGORITHMS类重编译:运行时状态校验走C层GlobalStateGuard,不经过guard_filter_fn通道 | https://gitcode.com/Ascend/pytorch/issues/4833 | 文档/功能限制 |
12+| 4 | 【26.2.0众测】PyTorch 2.9.0 Dynamo:输入张量带 `_dynamo_dynamic_indices`(list)触发重编译时抛 InternalTorchDynamoError(add_dynamic_indices_guard 期望 set 收到 list),建议同步上游 | https://gitcode.com/Ascend/pytorch/issues/5122 | Bug/崩溃(已验证纯CPU复现) |
13+ 
14+## Issue 1 要点
15+ 
16+- 环境:Atlas 800T A2(910B3)/ CANN 9.1.0 / torch 2.9.0 + torch_npu 2.9.0.post6 /
17+ triton 3.5.0 + triton_ascend 3.2.2 / torch-mlir 0.0.1(repo.oepkgs.net)
18+- 现象:MLIR 模式整图编译 resnet18,输出与 Eager 差异 0.28~2.0(跨进程幅度不稳定),
19+ 输出量级改变(eager absmax=0.16 vs mlir 0.71)
20+- 已二分定位:逐算子/单 stage/小组合全部正确;失败临界点为
21+ stem+layer1+layer2+layer3 编译为同一张图;伴随大量 Kernel compiled failed 静默回退
22+- 规避方案(已验证):`torch.compile(..., options={"npu_backend": "mlir"}, dynamic=True)`
23+ 完全修复(max_diff=0.000000);定位实验详见 `MLIR问题定位/`
24+ 
25+## Issue 2 要点
26+ 
27+- Inductor 默认后端(Triton)推理比 Eager 慢 25~79 倍(bs=8/32/64 三组数据),
28+ batch 越大退化越严重
29+- 已排除:图断裂/dynamic shape/首次编译开销;bf16、reduce-overhead、dynamic=False
30+ 均无法规避 → 慢在 Triton 生成 kernel 的设备执行时间
31+- 对照组:DVM 1.38x / NPUGraph_EX 1.47x / TorchAir-GE 1.95x 均有收益
32+ 
33+## Issue 3 要点
34+ 
35+- Guard Filter 文档示例2宣称"切换grad mode不会触发重编译",实测无法实现
36+- 根因:运行时状态校验走 C 层 GlobalStateGuard(torch/_dynamo/guards.py:2433),
37+ 不经过 guard_filter_fn 通道;TORCH_LOGS=recompiles 显示失效 guard 为
38+ GLOBAL_STATE changed
39+- 对照组:dict 类/按名/全局变量 guard 过滤均有效(证明通道本身正常)
40+ 
41+## 材料 PR
42+ 
43+- https://gitcode.com/Ascend/pytorch-ecosystem/merge_requests/38
44+ (源 `gcw_8p1hhlB0:master` → `Ascend/pytorch-ecosystem:master`)
45+ 
46+## 查重说明(2026-09-25,按群公告要求补充)
47+ 
48+- #4831(MLIR)与 #4793/#4836/#5063/#5116 属同一底层 bug 的不同模型/表现,
49+ 已在 #4831 评论中关联;本 issue 的增量价值:根因定位(融合kernel调优失败→
50+ FX回退不写预分配buffer)+ dynamic=True 规避方案 + 双版本验证
51+- #4833(GuardFilter)与 #4767 及后续同类相关,已在 #4833 评论中关联;
52+ 增量价值:C 层 GlobalStateGuard 源码级定位 + 过滤通道有效性对照组
53+- #4832(Triton 性能)为该问题首个报告(后续 #4851 为同类)
54+- #5122(Dynamo 崩溃):查重无同类,独立新问题
55+ 
56+## 补充说明
57+ 
58+调试提交 API 过程中曾误创建 23 个重复测试 issue(#4808~#4830),已全部改名为
59+"[重复-请忽略]"前缀标记以便甄别;正式 issue 为上表 4 个,带来困扰深表歉意。
@@ -0,0 +1,81 @@
1+#!/bin/bash
2+# TorchNPU 众测任务2(torch.compile)一键运行脚本(双环境版)
3+#
4+# 环境策略(按官方配套表,均为 TorchNPU 26.1.0 配套组合):
5+# - 示例 01~06(Guard Filter):venv PyTorch 2.9.0 + torch_npu 2.9.0.post6
6+# (Guard Filter 文档要求 PyTorch >= 2.9.0,guard_filter_fn 选项 2.9.0 才引入)
7+# - 示例 07~13(7 个编译后端):系统 PyTorch 2.7.1 + torch_npu 2.7.1.post8
8+# (环境预装的配套版本)
9+#
10+# 用法:
11+# ./run_task3.sh 01 # 按序号运行单个示例(01~06 Guard Filter,07~13 各后端)
12+# ./run_task3.sh all # 顺序运行全部 13 个示例
13+#
14+# 说明:
15+# - 自动设置 TORCHINDUCTOR_COMPILE_THREADS=8(默认 32 会在 32GB 容器内 OOM 被杀)
16+# - 编译缓存:首次编译 Triton 约 200s、MLIR 约 40 分钟,缓存后约 30~90s
17+ 
18+cd "$(dirname "$0")" # 补充-测试报告与原始日志/
19+source ./env_setup.sh
20+cd .. # 材料根目录
21+ 
22+export TORCHINDUCTOR_CACHE_DIR="${TORCHINDUCTOR_CACHE_DIR:-/tmp/inductor_cache}"
23+export TORCHINDUCTOR_COMPILE_THREADS=8
24+# 解释器:支持环境变量覆盖;自动探测常见位置(搭建方法见 环境搭建.md)
25+PY29="${PY29:-}" # Guard Filter 专用(torch 2.9.0 + torch_npu 2.9.0.post6 venv)
26+if [ -z "$PY29" ]; then
27+ for c in /root/venv_torch29/bin/python "$HOME/venv_torch29/bin/python" "$PWD/venv_torch29/bin/python"; do
28+ [ -x "$c" ] && PY29="$c" && break
29+ done
30+fi
31+PY27="${PY27:-}" # 后端示例用(torch 2.7.1 + torch_npu 2.7.1.post8,众测镜像预装)
32+if [ -z "$PY27" ]; then
33+ if python3.11 -c "import torch_npu" >/dev/null 2>&1; then PY27=python3.11
34+ elif python3 -c "import torch_npu" >/dev/null 2>&1; then PY27=python3
35+ else PY27=python3.11; fi
36+fi
37+T2="任务2-torch.compile体验与实践"
38+ 
39+V27=$($PY27 -c "import torch_npu; print(torch_npu.__version__)" 2>/dev/null | tail -1)
40+V29=$($PY29 -c "import torch; print(torch.__version__)" 2>/dev/null | tail -1)
41+echo "[run_task3] 后端环境: $PY27 + torch_npu ${V27:-缺失} | GuardFilter环境: ${PY29} + torch ${V29:-缺失} | threads=$TORCHINDUCTOR_COMPILE_THREADS"
42+if [[ ! "$V29" =~ ^2\.(9|1[0-9])\. ]]; then
43+ echo "[run_task3] 警告: Guard Filter 需要 PyTorch >= 2.9.0 的 venv(当前 ${V29:-无}),示例01~06将无法运行;搭建方法见 环境搭建.md(也可用 PY29=<路径> 指定)" >&2
44+fi
45+ 
46+declare -A BACKEND=(
47+ [07]="Inductor-Triton" [08]="Inductor-MLIR" [09]="Inductor-DVM"
48+ [10]="NPUGraphs" [11]="NPUGraph_EX" [12]="AOT_Eager" [13]="TorchAir-GE"
49+)
50+ 
51+run_one() {
52+ local n="$1" script=""
53+ local py=""
54+ if [[ "$n" =~ ^0[1-6]$ ]]; then
55+ local m="${n#0}" # 01~06 → 1~6(Guard Filter 脚本命名为 1_~6_,无前导零)
56+ script=$(ls "$T2/Guard Filter/${m}_"*.py 2>/dev/null | head -1)
57+ py="$PY29"
58+ elif [[ -n "${BACKEND[$n]:-}" ]]; then
59+ script="$T2/${BACKEND[$n]}/1_改写脚本.py"
60+ py="$PY27"
61+ fi
62+ if [ -z "$script" ] || [ ! -f "$script" ]; then
63+ echo "[run_task3] 错误: 未找到示例 $n 对应脚本" >&2
64+ return 1
65+ fi
66+ echo "==================== 示例 $n: $script ===================="
67+ "$py" "$script"
68+}
69+ 
70+ARG="${1:-}"
71+if [ "$ARG" = "all" ]; then
72+ for n in 01 02 03 04 05 06 07 08 09 10 11 12 13; do
73+ run_one "$n" || echo "[run_task3] 示例 $n 运行失败(继续下一个)"
74+ done
75+ echo "[run_task3] 全部完成"
76+elif [[ "$ARG" =~ ^[0-9]{2}$ ]]; then
77+ run_one "$ARG"
78+else
79+ echo "用法: $0 <01~13|all>"
80+ exit 1
81+fi