已合并
【26.2.0众测】ArgoNorthrop 任务1+任务2 交付件 #39
【26.2.0众测】ArgoNorthrop 任务1+任务2 交付件 #39
已合并
程康博创建于 9月15日
共 54 个文件变更+1985-0
@@ -0,0 +1,64 @@
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.1 软件安装证明材料(存放于「软件安装/」文件夹)
29+ -----------------------------------------------------------------
30+ 文件1:1_pip_list截图.png
31+ - 内容:终端执行 pip list 的输出截图,需能看到 torch、torch_npu 等包
32+ - 要求:截图清晰,包名和版本号可读
33+ - 文件格式:PNG 或 JPG
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+ - 要求:输出如下类似信息说明安装成功。截图清晰。
39+ tensor([[-0.6066, 6.3385, 0.0379, 3.3356],
40+ [ 2.9243, 3.3134, -1.5465, 0.1916],
41+ [-2.1807, 0.2008, -1.1431, 2.1523]], device='npu:0')
42+ - 文件格式:PNG 或 JPG
43+ 
44+ ★ 4.2 快速入门证明材料(存放于「快速入门/」文件夹)
45+ -----------------------------------------------------------------
46+ 文件1:1_训练脚本.py
47+ - 内容:您实际运行的训练脚本
48+ - 要求:脚本可独立运行,包含必要的注释说明
49+ - 文件命名:1_训练脚本.py
50+ - 文件格式:.py
51+ 
52+ 文件2:2_训练成功截图.png
53+ - 内容:训练脚本正常执行完毕的终端截图
54+ - 要求:能看到训练正常输出(loss 打印、epoch 进度等),无报错中断
55+ - 文件格式:PNG 或 JPG
56+ 
57+ 文件3:3_权重文件截图.png
58+ - 内容:生成的 checkpoint.pth.tar 权重文件的截图
59+ - 要求:可通过 ls -l 命令展示文件存在及大小,或文件管理器截图
60+ - 文件格式:PNG 或 JPG
61+ - 注意:权重文件本身较大,无需上传,只需截图证明已生成
62+ 
63+
64+=====================================================================
@@ -0,0 +1,77 @@
1+# 引入模块
2+import time
3+import torch
4+import torch.nn as nn
5+from torch.utils.data import DataLoader
6+import torchvision
7+ 
8+# =========【迁移新增:使能自动迁移】=========
9+import torch_npu
10+from torch_npu.contrib import transfer_to_npu
11+ 
12+# 初始化运行device(自动迁移方案:GPU代码原样保留cuda:0,不修改!)
13+device = torch.device('cuda:0')
14+ 
15+# 定义模型网络【GPU原版代码,完全不变】
16+class CNN(nn.Module):
17+ def __init__(self):
18+ super(CNN, self).__init__()
19+ self.net = nn.Sequential(
20+ nn.Conv2d(in_channels=1, out_channels=16,
21+ kernel_size=(3, 3),
22+ stride=(1, 1),
23+ padding=1),
24+ nn.MaxPool2d(kernel_size=2),
25+ nn.Conv2d(16, 32, 3, 1, 1),
26+ nn.MaxPool2d(kernel_size=2),
27+ nn.Flatten(),
28+ nn.Linear(32*7*7, 16),
29+ nn.ReLU(),
30+ nn.Linear(16, 10)
31+ )
32+ def forward(self, x):
33+ return self.net(x)
34+ 
35+# 下载数据集【与官方快速入门文档示例一致】
36+train_data = torchvision.datasets.MNIST(
37+ root='mnist',
38+ download=True,
39+ train=True,
40+ transform=torchvision.transforms.ToTensor()
41+)
42+ 
43+# 定义训练相关参数【GPU原版代码,完全不变】
44+batch_size = 64
45+model = CNN().to(device)
46+train_dataloader = DataLoader(train_data, batch_size=batch_size)
47+loss_func = nn.CrossEntropyLoss().to(device)
48+optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
49+epochs = 10
50+ 
51+# 设置循环【GPU原版代码,完全不变:Atlas A2 可不开启AMP】
52+for epoch in range(epochs):
53+ running_loss = 0.0
54+ for imgs, labels in train_dataloader:
55+ start_time = time.time() # 记录训练开始时间
56+ imgs = imgs.to(device) # 把img数据放到指定设备上
57+ labels = labels.to(device) # 把label数据放到指定设备上
58+ outputs = model(imgs) # 前向计算
59+ loss = loss_func(outputs, labels) # 损失函数计算
60+ optimizer.zero_grad()
61+ loss.backward() # 损失函数反向计算
62+ optimizer.step() # 更新优化器
63+ running_loss += loss.item()
64+ # 打印每个 epoch 的平均 loss,便于截图确认训练正常推进
65+ print(f"Epoch [{epoch+1}/{epochs}] loss={running_loss/len(train_dataloader):.4f} "
66+ f"耗时={time.time()-start_time:.2f}s/最后batch", flush=True)
67+ 
68+print("训练完成,开始保存权重 checkpoint.pth.tar ...", flush=True)
69+ 
70+# 定义保存模型【GPU原版代码,完全不变】
71+torch.save({
72+ 'epoch': 10,
73+ 'arch': CNN,
74+ 'state_dict': model.state_dict(),
75+ 'optimizer' : optimizer.state_dict(),
76+},'checkpoint.pth.tar')
77+print("权重已保存:checkpoint.pth.tar", flush=True)
@@ -0,0 +1,99 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · AOT_Eager(AOT Autograd + Eager,无图融合/算子编译)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
7+ 
8+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8):
9+ env -u CI OMP_NUM_THREADS=1 TORCHINDUCTOR_COMPILE_THREADS=1 \
10+ HF_HOME=/root/.cache/huggingface python 1_改写脚本.py
11+"""
12+import os
13+os.environ.setdefault("OMP_NUM_THREADS", "1")
14+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
15+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
16+ 
17+import time
18+import torch
19+import torch_npu # noqa: F401 导入即注册 NPU 后端
20+from transformers import DistilBertForMaskedLM
21+ 
22+BACKEND_NAME = "aot_eager"
23+BATCH, SEQ_LEN = 4, 128
24+WARMUP, ITERS = 3, 20
25+ 
26+ 
27+def sync():
28+ torch.npu.synchronize()
29+ 
30+ 
31+def bench(fn, warmup=WARMUP, iters=ITERS):
32+ with torch.no_grad():
33+ for _ in range(warmup):
34+ fn()
35+ sync()
36+ t0 = time.perf_counter()
37+ for _ in range(iters):
38+ fn()
39+ sync()
40+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
41+ 
42+ 
43+def main():
44+ torch.npu.set_device(0)
45+ print("=" * 72)
46+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
47+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
48+ f"device={torch.npu.get_device_name(0)}")
49+ print("=" * 72)
50+ 
51+ # 1) 加载模型与输入 ----------------------------------------------------
52+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
53+ model.to("npu").eval()
54+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
55+ attention_mask = torch.ones_like(input_ids).npu()
56+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}")
57+ 
58+ # 2) Eager 基线 -------------------------------------------------------
59+ def run_eager():
60+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
61+ 
62+ t_eager = bench(run_eager)
63+ with torch.no_grad():
64+ out_eager = run_eager()
65+ 
66+ # 3) 编译后端 ---------------------------------------------------------
67+ t0 = time.perf_counter()
68+ compiled = torch.compile(model, backend=BACKEND_NAME)
69+ with torch.no_grad():
70+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次触发编译
71+ sync()
72+ t_compile = (time.perf_counter() - t0) * 1e3
73+ 
74+ def run_compiled():
75+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
76+ 
77+ t_compiled = bench(run_compiled)
78+ with torch.no_grad():
79+ out_compiled = run_compiled()
80+ 
81+ # 4) 正确性 -----------------------------------------------------------
82+ maxdiff = (out_eager - out_compiled).abs().max().item()
83+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
84+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
85+ speedup = t_eager / t_compiled
86+ print("\n[正确性] Eager vs 编译输出:")
87+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
88+ print(f" allclose(atol=1e-5) = {ok5}")
89+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
90+ print(f" 首次编译耗时 = {t_compile:9.1f} ms")
91+ print(f" Eager = {t_eager:9.3f} ms/次")
92+ print(f" {BACKEND_NAME:14s}= {t_compiled:9.3f} ms/次")
93+ print(f" 加速比 = {speedup:9.2f}x")
94+ print("\n结论:{},相对 Eager {:.2f}x。".format(
95+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
96+ 
97+ 
98+if __name__ == "__main__":
99+ main()
@@ -0,0 +1,156 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · Guard Filter · 示例1:过滤“字典版本 / 键集合”类 guard
4+================================================================
5+被测模型:distilbert-base-uncased(DistilBertForMaskedLM,MLM 头,离线 HF 缓存)
6+说明:本脚本没有任何自建网络模型——编译对象就是一个普通 Python 函数,函数内部
7+ 调用真实的 DistilBertForMaskedLM,仅在其 logits 上叠加一个生成配置读取。
8+ 
9+场景(还原 HuggingFace generation_config 字典的典型用法):
10+ 通过 cfg.get("temperature", 默认值) 读取一个“当前不存在”的键。Dynamo 会对
11+ 该字典安装 DICT_CONTAINS / DICT_KEYS_MATCH / NOT_PRESENT_IN_GENERIC_DICT 等
12+ guard;外部给字典补上这个键(字典 version 变化)后,默认行为就是【重新编译】。
13+ 该变化不影响复用同一份计算图,于是用 guard_filter_fn 把字典类 guard 过滤掉,
14+ 再次补键时直接命中缓存、不再重编译。
15+ 
16+判定方法:torch.compiler.set_stance("fail_on_recompile")
17+ —— 调用期间若发生重编译会抛 RuntimeError,据此判断。
18+运行:env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
19+ python 1_过滤字典版本guard.py
20+"""
21+import os
22+os.environ.setdefault("OMP_NUM_THREADS", "1")
23+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
24+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
25+ 
26+import time
27+import torch
28+import torch_npu # noqa: F401 导入即把 PyTorch 的后端桥接到 NPU
29+from transformers import DistilBertForMaskedLM
30+ 
31+torch.npu.set_device(0)
32+DEV = "npu"
33+BATCH, SEQ_LEN = 2, 64
34+ 
35+# 需要过滤的“字典版本/键集合”类 guard
36+DICT_GUARDS = frozenset({
37+ "DICT_VERSION", "DICT_KEYS", "DICT_KEYS_MATCH",
38+ "DICT_CONTAINS", "NOT_PRESENT_IN_GENERIC_DICT",
39+})
40+ 
41+ 
42+def fwd_with_cfg(bert, input_ids, attention_mask, cfg):
43+ """以真实 DistilBert 为核心的前向函数,仅叠加一个生成配置字典读取。"""
44+ logits = bert(input_ids=input_ids,
45+ attention_mask=attention_mask).logits
46+ temperature = cfg.get("temperature", 1.0) # 首次该键缺失 -> DICT_CONTAINS
47+ return logits * temperature
48+ 
49+ 
50+def make_filter(keep_rule, store):
51+ """构造 guard_filter_fn,同时把本次安装的 guard 记录到 store 里。"""
52+ 
53+ def guard_filter_fn(entries):
54+ store.clear()
55+ store.extend([(str(e.guard_type), str(e.name)) for e in entries])
56+ return [bool(keep_rule(e)) for e in entries]
57+ 
58+ return guard_filter_fn
59+ 
60+ 
61+def recompiles_on_changed_call(compiled, bert, ids, mask, cfg):
62+ """在 fail_on_recompile 姿态下调用;抛异常=重编译,未抛=命中缓存。"""
63+ try:
64+ with torch.compiler.set_stance("fail_on_recompile"):
65+ compiled(bert, ids, mask, cfg)
66+ return False
67+ except RuntimeError:
68+ return True
69+ 
70+ 
71+def sync():
72+ torch.npu.synchronize()
73+ 
74+ 
75+def first_change_ms(compiled, bert, ids, mask, cfg):
76+ """状态切换后的“当次调用”耗时(baseline 当次含重编译,过滤后命中缓存)。"""
77+ sync()
78+ t0 = time.perf_counter()
79+ with torch.no_grad():
80+ compiled(bert, ids, mask, cfg)
81+ sync()
82+ return (time.perf_counter() - t0) * 1e3
83+ 
84+ 
85+def run(bert, ids, mask, use_filter):
86+ torch._dynamo.reset()
87+ cfg = {"do_sample": False} # 注意:此时没有 "temperature" 键
88+ entries = []
89+ if use_filter:
90+ rule = lambda e: str(e.guard_type) not in DICT_GUARDS
91+ opt = {"guard_filter_fn": make_filter(rule, entries)}
92+ tag = "过滤后"
93+ else:
94+ opt = {"guard_filter_fn": make_filter(lambda e: True, entries)}
95+ tag = "未过滤"
96+ 
97+ compiled = torch.compile(fwd_with_cfg, backend="aot_eager", options=opt)
98+ with torch.no_grad():
99+ compiled(bert, ids, mask, cfg) # 首次调用 -> 触发编译
100+ dict_guards = sorted({t for t, _ in entries if t in DICT_GUARDS})
101+ cfg["temperature"] = 0.9 # 外部补键 -> version 变化
102+ rec = recompiles_on_changed_call(compiled, bert, ids, mask, cfg)
103+ changed_ms = first_change_ms(compiled, bert, ids, mask, cfg)
104+ return tag, dict_guards, rec, changed_ms
105+ 
106+ 
107+def main():
108+ print("=" * 72)
109+ print("示例1:过滤字典版本 / 键集合类 guard(基于 distilbert-base-uncased)")
110+ print("=" * 72)
111+ 
112+ bert = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
113+ bert.to(DEV).eval()
114+ input_ids = torch.randint(0, bert.config.vocab_size, (BATCH, SEQ_LEN)).npu()
115+ attention_mask = torch.ones_like(input_ids).npu()
116+ print(f"被测模型:DistilBertForMaskedLM 输入:input_ids{tuple(input_ids.shape)}")
117+ 
118+ tag_b, guards_b, rec_b, ms_b = run(bert, input_ids, attention_mask, False)
119+ tag_f, guards_f, rec_f, ms_f = run(bert, input_ids, attention_mask, True)
120+ 
121+ # 1) guard 枚举
122+ print("\n[1] 编译期安装的字典类 guard:")
123+ print(" ", guards_b if guards_b else "(未观察到)")
124+ 
125+ # 2) 重编译消除验证
126+ print("\n[2] 给配置字典补上缺失键 temperature 后,是否触发重编译:")
127+ print(f" {tag_b}:{'发生重编译 ❗' if rec_b else '未重编译 ✅'}")
128+ print(f" {tag_f}:{'发生重编译 ❗' if rec_f else '未重编译 ✅(已命中缓存)'}")
129+ print(" 结论:{}".format(
130+ "过滤字典 guard 成功消除重编译 🎉" if (rec_b and not rec_f) else "请检查环境/脚本"))
131+ 
132+ # 3) 正确性:稳定配置下,过滤后编译结果 vs Eager 结果(DistilBert logits)
133+ torch._dynamo.reset()
134+ with torch.no_grad():
135+ cfg_ok = {"do_sample": False}
136+ eager_out = fwd_with_cfg(bert, input_ids, attention_mask, cfg_ok)
137+ rule = lambda entries: [str(e.guard_type) not in DICT_GUARDS for e in entries]
138+ cf = torch.compile(fwd_with_cfg, backend="aot_eager",
139+ options={"guard_filter_fn": rule})
140+ compiled_out = cf(bert, input_ids, attention_mask, cfg_ok)
141+ maxdiff = (eager_out - compiled_out).abs().max().item()
142+ ok4 = torch.allclose(eager_out, compiled_out, atol=1e-4)
143+ print(f"\n[3] 正确性:Eager vs 过滤后编译(DistilBert logits)")
144+ print(f" max|Δ| = {maxdiff:.3e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
145+ 
146+ # 4) 性能:配置变化后的稳态调用(未过滤含重编译 vs 过滤后命中缓存)
147+ print("\n[4] 性能:字典补键后的当次调用耗时:")
148+ print(f" {tag_b}(含重编译):{ms_b:8.3f} ms")
149+ print(f" {tag_f}(缓存命中):{ms_f:8.3f} ms")
150+ print(f" 加速比 ≈ {ms_b / max(ms_f, 1e-6):8.2f}x")
151+ print("\n说明:guard_filter_fn 属于 unsafe 能力,请仅在确认这些变化不影响")
152+ print(" 计算图语义时使用(如生成配置、元信息字典等)。")
153+ 
154+ 
155+if __name__ == "__main__":
156+ main()
@@ -0,0 +1,195 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · Guard Filter · 示例2:过滤“运行时状态”类 guard
4+================================================================
5+被测模型:distilbert-base-uncased(DistilBertForMaskedLM,离线 HF 缓存)
6+说明:本脚本没有任何自建网络模型——部分 A 直接编译 DistilBertForMaskedLM 实例,
7+ 部分 B 编译一个内部调用 DistilBertForMaskedLM 的普通函数。
8+文档给出的运行时状态 guard 包括:
9+ GRAD_MODE / TORCH_FUNCTION_STATE / GLOBAL_STATE / DEFAULT_DEVICE /
10+ DETERMINISTIC_ALGORITHMS / AUTOCAST_STATE / FSDP_TRAINING_STATE
11+本脚本分两部分:
12+ A. 严格按文档复现:编译 DistilBert 后用 torch.no_grad() 切换 GRAD_MODE,
13+ 观察过滤行为(如实记录 torch 2.9.0 + torch_npu 上的现象,作为众测反馈)。
14+ B. 给出一个可真正消除重编译的“应用级运行时状态”示例(Python 层局部状态,
15+ 对应 CONSTANT_MATCH guard),状态切换作用于 DistilBert logits 的后处理。
16+ 
17+运行:env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
18+ python 2_过滤运行时状态guard.py
19+"""
20+import os
21+os.environ.setdefault("OMP_NUM_THREADS", "1")
22+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
23+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
24+ 
25+import time
26+import torch
27+import torch_npu # noqa: F401
28+from transformers import DistilBertForMaskedLM
29+ 
30+torch.npu.set_device(0)
31+DEV = "npu"
32+BATCH, SEQ_LEN = 2, 64
33+ 
34+# 文档列出的 7 类运行时状态 guard
35+RUNTIME_GUARDS = frozenset({
36+ "GRAD_MODE", "TORCH_FUNCTION_STATE", "GLOBAL_STATE", "DEFAULT_DEVICE",
37+ "DETERMINISTIC_ALGORITHMS", "AUTOCAST_STATE", "FSDP_TRAINING_STATE",
38+})
39+ 
40+ 
41+def make_filter(keep_rule, store):
42+ def guard_filter_fn(entries):
43+ store.clear()
44+ keeps = [bool(keep_rule(e)) for e in entries]
45+ store.extend([(str(e.guard_type), str(e.name), k)
46+ for e, k in zip(entries, keeps)])
47+ return keeps
48+ return guard_filter_fn
49+ 
50+ 
51+def sync():
52+ torch.npu.synchronize()
53+ 
54+ 
55+def first_change_ms(call):
56+ """状态切换后的“当次调用”耗时(baseline 当次含重编译,过滤后命中缓存)。"""
57+ sync()
58+ t0 = time.perf_counter()
59+ call()
60+ sync()
61+ return (time.perf_counter() - t0) * 1e3
62+ 
63+ 
64+# ----------------------------------------------------------------------
65+# 部分 A:GRAD_MODE 文档复现(真实 DistilBert)
66+# ----------------------------------------------------------------------
67+def part_a(bert, ids, mask):
68+ print("-" * 72)
69+ print("部分 A:文档示例复现 —— 过滤 GRAD_MODE(DistilBert,torch.no_grad 切换)")
70+ print("-" * 72)
71+ 
72+ def once(use_filter):
73+ torch._dynamo.reset()
74+ store = []
75+ if use_filter:
76+ rule = lambda e: str(e.guard_type) not in RUNTIME_GUARDS
77+ opt = {"guard_filter_fn": make_filter(rule, store)}
78+ else:
79+ opt = {"guard_filter_fn": make_filter(lambda e: True, store)}
80+ cf = torch.compile(bert, backend="aot_eager", options=opt)
81+ cf(input_ids=ids, attention_mask=mask) # grad-enabled 下首次编译
82+ in_grad = any(t == "GRAD_MODE" for t, _, _ in store)
83+ kept_grad = any(t == "GRAD_MODE" and k for t, _, k in store)
84+ with torch.no_grad():
85+ try:
86+ with torch.compiler.set_stance("fail_on_recompile"):
87+ cf(input_ids=ids, attention_mask=mask)
88+ rec = False
89+ except RuntimeError:
90+ rec = True
91+ return in_grad, kept_grad, len(store), rec
92+ 
93+ ib, kb, nb, rb = once(False)
94+ if_, kf, nf, rf = once(True)
95+ print(f" 未过滤:guard 总数={nb:4d},收到 GRAD_MODE={ib},保留 GRAD_MODE={kb},"
96+ f"no_grad 后重编译={rb}")
97+ print(f" 过滤后:guard 总数={nf:4d},收到 GRAD_MODE={if_},保留 GRAD_MODE={kf},"
98+ f"no_grad 后重编译={rf}")
99+ print(" 现象说明:guard_filter_fn 已把 GRAD_MODE 从 guard 列表中移除,"
100+ "但在 torch 2.9.0 +")
101+ print(" torch_npu 上,AOTAutograd 仍会按 no_grad / 训练态重新特化前反向图,")
102+ print(" 因此该调用仍被视为重编译。该现象已作为本次众测 Issue 反馈。")
103+ 
104+ 
105+# ----------------------------------------------------------------------
106+# 部分 B:可真正消除重编译的“应用级运行时状态”(Python 层,CONSTANT_MATCH)
107+# ----------------------------------------------------------------------
108+def part_b(bert, ids, mask):
109+ print("-" * 72)
110+ print("部分 B:应用级运行时状态(局部状态对象,作用于 DistilBert logits)")
111+ print("-" * 72)
112+ 
113+ def once(use_filter):
114+ torch._dynamo.reset()
115+ phase = {"v": 1} # 应用运行时状态:1=预热阶段,2=正式阶段(局部对象)
116+ 
117+ def f(input_ids, attention_mask):
118+ logits = bert(input_ids=input_ids,
119+ attention_mask=attention_mask).logits
120+ return torch.relu(logits) if phase["v"] == 1 else logits + 1.0
121+ 
122+ store = []
123+ if use_filter:
124+ rule = lambda e: str(e.guard_type) != "CONSTANT_MATCH"
125+ opt = {"guard_filter_fn": make_filter(rule, store)}
126+ tag = "过滤后"
127+ else:
128+ opt = {"guard_filter_fn": make_filter(lambda e: True, store)}
129+ tag = "未过滤"
130+ cf = torch.compile(f, backend="aot_eager", options=opt)
131+ with torch.no_grad():
132+ cf(ids, mask) # 阶段1:首次编译
133+ cm = [f"{k}({n})" for k, n, _ in store if k == "CONSTANT_MATCH"]
134+ phase["v"] = 2 # 运行时切换阶段
135+ try:
136+ with torch.compiler.set_stance("fail_on_recompile"):
137+ cf(ids, mask)
138+ rec = False
139+ except RuntimeError:
140+ rec = True
141+ ms = first_change_ms(lambda: cf(ids, mask))
142+ return tag, cm, rec, ms, f
143+ 
144+ tag_b, cm_b, rec_b, ms_b, _ = once(False)
145+ tag_f, cm_f, rec_f, ms_f, f_f = once(True)
146+ 
147+ print("\n[1] 编译期与运行时状态相关的 guard:")
148+ print(" ", cm_b[:5], "..." if len(cm_b) > 5 else "")
149+ print("\n[2] 运行时切换阶段后是否重编译:")
150+ print(f" {tag_b}:{'发生重编译 ❗' if rec_b else '未重编译 ✅'}")
151+ print(f" {tag_f}:{'发生重编译 ❗' if rec_f else '未重编译 ✅(命中缓存)'}")
152+ print(" 结论:{}".format(
153+ "过滤应用运行时状态 guard 成功消除重编译 🎉" if (rec_b and not rec_f) else "请检查"))
154+ 
155+ # 正确性:阶段1 稳定状态下,Eager 与过滤后编译的 DistilBert logits 一致
156+ phase = {"v": 1}
157+ 
158+ def eager_f(input_ids, attention_mask):
159+ logits = bert(input_ids=input_ids, attention_mask=attention_mask).logits
160+ return torch.relu(logits) if phase["v"] == 1 else logits + 1.0
161+ 
162+ torch._dynamo.reset()
163+ rule = lambda entries: [str(e.guard_type) != "CONSTANT_MATCH" for e in entries]
164+ cf = torch.compile(eager_f, backend="aot_eager",
165+ options={"guard_filter_fn": rule})
166+ with torch.no_grad():
167+ out_e = eager_f(ids, mask)
168+ out_c = cf(ids, mask)
169+ maxdiff = (out_e - out_c).abs().max().item()
170+ ok4 = torch.allclose(out_e, out_c, atol=1e-4)
171+ print(f"\n[3] 正确性:Eager vs 过滤后编译(DistilBert logits)")
172+ print(f" max|Δ| = {maxdiff:.3e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
173+ 
174+ print("\n[4] 性能:阶段切换当次调用耗时:")
175+ print(f" {tag_b}(含重编译):{ms_b:8.3f} ms")
176+ print(f" {tag_f}(缓存命中):{ms_f:8.3f} ms")
177+ print(f" 加速比 ≈ {ms_b / max(ms_f, 1e-6):8.2f}x")
178+ 
179+ 
180+def main():
181+ print("=" * 72)
182+ print("示例2:过滤运行时状态类 guard(基于 distilbert-base-uncased)")
183+ print("=" * 72)
184+ bert = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
185+ bert.to(DEV).eval()
186+ ids = torch.randint(0, bert.config.vocab_size, (BATCH, SEQ_LEN)).npu()
187+ mask = torch.ones_like(ids).npu()
188+ print(f"模型:DistilBertForMaskedLM 输入:input_ids{tuple(ids.shape)}")
189+ part_a(bert, ids, mask)
190+ print()
191+ part_b(bert, ids, mask)
192+ 
193+ 
194+if __name__ == "__main__":
195+ main()
@@ -0,0 +1,167 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · Guard Filter · 示例3:组合过滤多类 guard
4+================================================================
5+被测模型:distilbert-base-uncased(DistilBertForMaskedLM,离线 HF 缓存)
6+说明:本脚本没有任何自建网络模型——编译对象是一个普通 Python 函数,内部调用真实
7+ 的 DistilBertForMaskedLM,在其 logits 上模拟生成式模型 prefill->decode 常见
8+ 的多类“易变”guard:
9+ 1) 配置字典 cfg.get(...) -> DICT_CONTAINS / DICT_KEYS / DICT_VERSION
10+ 2) 可选缓存 past(None <-> Tensor)-> TYPE_MATCH / OPTIONAL_TENSOR
11+ 3) 全局特性开关 GLOBAL_FLAG -> EQUALS_MATCH(is_global=True)
12+ 4) 运行时状态 -> GRAD_MODE 等
13+ 5) 内建对象/属性 -> HASATTR / NOT_PRESENT_IN_GENERIC_DICT / CONSTANT_MATCH
14+分别过滤每一类很繁琐,这里用一个 guard_filter_fn 一次性组合过滤。
15+ 
16+prefill 阶段:past=None、配置缺键;decode 阶段:past=Tensor(与 logits 同形的
17+缓存增量)、配置补键。默认从 prefill 切到 decode 会触发重编译;组合过滤后直接
18+复用同一份图。
19+ 
20+运行:env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
21+ python 3_组合多类guard过滤.py
22+"""
23+import os
24+os.environ.setdefault("OMP_NUM_THREADS", "1")
25+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
26+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
27+ 
28+import time
29+import torch
30+import torch_npu # noqa: F401
31+from transformers import DistilBertForMaskedLM
32+ 
33+torch.npu.set_device(0)
34+DEV = "npu"
35+BATCH, SEQ_LEN = 2, 64
36+ 
37+GLOBAL_FLAG = {"mode": 1} # 全局特性开关(is_global=True)
38+ 
39+_DICT = frozenset({"DICT_VERSION", "DICT_KEYS", "DICT_KEYS_MATCH", "DICT_CONTAINS",
40+ "NOT_PRESENT_IN_GENERIC_DICT"})
41+_RUNTIME = frozenset({"GRAD_MODE", "TORCH_FUNCTION_STATE", "GLOBAL_STATE", "DEFAULT_DEVICE",
42+ "DETERMINISTIC_ALGORITHMS", "AUTOCAST_STATE", "FSDP_TRAINING_STATE"})
43+_OTHER = frozenset({"TYPE_MATCH", "OPTIONAL_TENSOR", "HASATTR",
44+ "CONSTANT_MATCH", "EQUALS_MATCH"})
45+ 
46+ 
47+def fwd_prefill_decode(bert, input_ids, attention_mask, past=None, cfg=None):
48+ """以真实 DistilBert 为核心:可选缓存增量 + 配置字典 + 全局开关。"""
49+ logits = bert(input_ids=input_ids,
50+ attention_mask=attention_mask).logits
51+ h = logits + past if past is not None else logits # past: None <-> Tensor
52+ if GLOBAL_FLAG["mode"] == 1: # 全局开关
53+ h = torch.relu(h)
54+ scale = cfg.get("temperature", 1.0) if cfg is not None else 1.0
55+ return h * scale
56+ 
57+ 
58+# 组合规则:命中上述任意一类,或属于全局变量 guard 的,一律过滤掉(返回 False)
59+def combo_rule(entry):
60+ t = str(entry.guard_type)
61+ return not (t in _DICT or t in _RUNTIME or t in _OTHER or entry.is_global)
62+ 
63+ 
64+def make_filter(keep_rule, store):
65+ def guard_filter_fn(entries):
66+ store.clear()
67+ keeps = [bool(keep_rule(e)) for e in entries]
68+ store.extend([(str(e.guard_type), str(e.name), bool(e.is_global), k)
69+ for e, k in zip(entries, keeps)])
70+ return keeps
71+ return guard_filter_fn
72+ 
73+ 
74+def sync():
75+ torch.npu.synchronize()
76+ 
77+ 
78+def first_change_ms(compiled, bert, ids, mask, past, cfg):
79+ """状态切换后的“当次调用”耗时(baseline 当次含重编译,过滤后命中缓存)。"""
80+ sync()
81+ t0 = time.perf_counter()
82+ with torch.no_grad():
83+ compiled(bert, ids, mask, past, cfg)
84+ sync()
85+ return (time.perf_counter() - t0) * 1e3
86+ 
87+ 
88+def run(bert, ids, mask, past, use_filter):
89+ torch._dynamo.reset()
90+ GLOBAL_FLAG["mode"] = 1
91+ cfg = {"do_sample": False} # prefill:缺 temperature 键
92+ store = []
93+ if use_filter:
94+ opt = {"guard_filter_fn": make_filter(combo_rule, store)}
95+ tag = "组合过滤后"
96+ else:
97+ opt = {"guard_filter_fn": make_filter(lambda e: True, store)}
98+ tag = "不过滤"
99+ cf = torch.compile(fwd_prefill_decode, backend="aot_eager", options=opt)
100+ 
101+ with torch.no_grad():
102+ cf(bert, ids, mask, None, cfg) # prefill:首次编译
103+ cfg["temperature"] = 0.8 # 字典补键
104+ try:
105+ with torch.compiler.set_stance("fail_on_recompile"):
106+ cf(bert, ids, mask, past, cfg) # decode:past 变 Tensor + 字典变化
107+ rec = False
108+ except RuntimeError:
109+ rec = True
110+ ms = first_change_ms(cf, bert, ids, mask, past, cfg)
111+ return tag, store, rec, ms
112+ 
113+ 
114+def main():
115+ print("=" * 72)
116+ print("示例3:组合过滤多类 guard(基于 distilbert-base-uncased,prefill/decode)")
117+ print("=" * 72)
118+ 
119+ bert = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
120+ bert.to(DEV).eval()
121+ ids = torch.randint(0, bert.config.vocab_size, (BATCH, SEQ_LEN)).npu()
122+ mask = torch.ones_like(ids).npu()
123+ with torch.no_grad():
124+ logits_shape = fwd_prefill_decode(bert, ids, mask).shape
125+ past = torch.randn(*logits_shape, device=DEV)
126+ print(f"被测模型:DistilBertForMaskedLM 输入:input_ids{tuple(ids.shape)} "
127+ f"past:None(prefill) -> Tensor{tuple(past.shape)}(decode)")
128+ 
129+ tag_b, store_b, rec_b, ms_b = run(bert, ids, mask, past, False)
130+ tag_f, store_f, rec_f, ms_f = run(bert, ids, mask, past, True)
131+ 
132+ # 1) 展示被组合规则过滤掉的 guard 类别
133+ dropped = sorted({t for t, _, g, k in store_f if not k})
134+ print("\n[1] 组合规则一次性过滤掉的 guard 类型:")
135+ print(" ", dropped)
136+ 
137+ # 2) 重编译消除
138+ print("\n[2] prefill -> decode(past: None->Tensor、配置补键)是否重编译:")
139+ print(f" {tag_b}:{'发生重编译 ❗' if rec_b else '未重编译 ✅'}")
140+ print(f" {tag_f}:{'发生重编译 ❗' if rec_f else '未重编译 ✅(prefill/decode 复用同一图)'}")
141+ print(" 结论:{}".format(
142+ "组合多类 guard 过滤成功消除重编译 🎉" if (rec_b and not rec_f) else "请检查"))
143+ 
144+ # 3) 正确性:prefill 稳定签名下 Eager 与组合过滤后编译一致(DistilBert logits)
145+ torch._dynamo.reset()
146+ GLOBAL_FLAG["mode"] = 1
147+ with torch.no_grad():
148+ cfg_ok = {"do_sample": False}
149+ cfc = torch.compile(fwd_prefill_decode, backend="aot_eager",
150+ options={"guard_filter_fn": lambda es: [combo_rule(e) for e in es]})
151+ out_e = fwd_prefill_decode(bert, ids, mask, None, cfg_ok)
152+ out_c = cfc(bert, ids, mask, None, cfg_ok)
153+ maxdiff = (out_e - out_c).abs().max().item()
154+ ok4 = torch.allclose(out_e, out_c, atol=1e-4)
155+ print(f"\n[3] 正确性(prefill 签名,DistilBert logits):")
156+ print(f" max|Δ| = {maxdiff:.3e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
157+ 
158+ # 4) 性能
159+ print("\n[4] 性能:prefill->decode 切换的当次调用耗时:")
160+ print(f" {tag_b}(含重编译):{ms_b:8.3f} ms")
161+ print(f" {tag_f}(缓存命中):{ms_f:8.3f} ms")
162+ print(f" 加速比 ≈ {ms_b / max(ms_f, 1e-6):8.2f}x")
163+ print("\n注意:组合过滤属于 unsafe,前提是 prefill/decode 复用语义一致(本例仅演示)。")
164+ 
165+ 
166+if __name__ == "__main__":
167+ main()
@@ -0,0 +1,141 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · Guard Filter · 示例4:按“变量名”精准过滤 guard(filter_by_name)
4+================================================================
5+被测模型:distilbert-base-uncased(DistilBertForMaskedLM,离线 HF 缓存)
6+说明:本脚本没有任何自建网络模型——编译对象是一个普通 Python 函数,内部调用
7+ 真实的 DistilBertForMaskedLM,仅给它增加一个可选偏置参数 y。
8+不按 guard 类型“一刀切”,而是根据 entry.name / entry.value 精准定位到某个变量:
9+ 首次调用 y=None,Dynamo 会针对“y 的值是 None”安装 CONSTANT_MATCH(y, None);
10+ 之后传入 y=Tensor 时默认触发重编译。
11+我们只过滤 name == "y" 且 value is None 的那一条 guard,其余 guard 全部保留,
12+是一种最小作用域、相对可控的过滤方式。
13+ 
14+运行:env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
15+ python 4_按变量名过滤.py
16+"""
17+import os
18+os.environ.setdefault("OMP_NUM_THREADS", "1")
19+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
20+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
21+ 
22+import time
23+import torch
24+import torch_npu # noqa: F401
25+from transformers import DistilBertForMaskedLM
26+ 
27+torch.npu.set_device(0)
28+DEV = "npu"
29+BATCH, SEQ_LEN = 2, 64
30+ 
31+ 
32+def make_filter(keep_rule, store):
33+ def guard_filter_fn(entries):
34+ store.clear()
35+ keeps = [bool(keep_rule(e)) for e in entries]
36+ # 只记录类型/变量名/是否为None,不 repr 设备张量值
37+ store.extend([(str(e.guard_type), str(e.name), getattr(e, "value", None) is None, k)
38+ for e, k in zip(entries, keeps)])
39+ return keeps
40+ return guard_filter_fn
41+ 
42+ 
43+# 仅过滤:变量名为 y 且当前取值为 None 的 guard
44+def name_rule(e):
45+ return not (e.name == "y" and e.value is None)
46+ 
47+ 
48+def sync():
49+ torch.npu.synchronize()
50+ 
51+ 
52+def first_change_ms(call):
53+ """状态切换后的“当次调用”耗时(baseline 当次含重编译,过滤后命中缓存)。"""
54+ sync()
55+ t0 = time.perf_counter()
56+ call()
57+ sync()
58+ return (time.perf_counter() - t0) * 1e3
59+ 
60+ 
61+def run(bert, ids, mask, y, use_filter):
62+ torch._dynamo.reset()
63+ 
64+ def f(input_ids, attention_mask, y=None):
65+ """DistilBert 前向 + 可选偏置:y 为 None 时走旁路,否则与 logits 相加。"""
66+ logits = bert(input_ids=input_ids, attention_mask=attention_mask).logits
67+ if y is not None:
68+ logits = logits + y
69+ return logits
70+ 
71+ store = []
72+ if use_filter:
73+ opt = {"guard_filter_fn": make_filter(name_rule, store)}
74+ tag = "按名过滤后"
75+ else:
76+ opt = {"guard_filter_fn": make_filter(lambda e: True, store)}
77+ tag = "不过滤"
78+ cf = torch.compile(f, backend="aot_eager", options=opt)
79+ with torch.no_grad():
80+ cf(ids, mask) # 首次:y=None
81+ target = [(t, n, v) for t, n, v, k in store if n == "y"]
82+ try:
83+ with torch.compiler.set_stance("fail_on_recompile"):
84+ cf(ids, mask, y) # y 变成 Tensor
85+ rec = False
86+ except RuntimeError:
87+ rec = True
88+ ms = first_change_ms(lambda: cf(ids, mask, y))
89+ return tag, target, rec, ms, f
90+ 
91+ 
92+def main():
93+ print("=" * 72)
94+ print("示例4:按变量名精准过滤 guard(基于 distilbert-base-uncased,只放过 y=None)")
95+ print("=" * 72)
96+ 
97+ bert = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
98+ bert.to(DEV).eval()
99+ ids = torch.randint(0, bert.config.vocab_size, (BATCH, SEQ_LEN)).npu()
100+ mask = torch.ones_like(ids).npu()
101+ with torch.no_grad():
102+ vocab = bert.config.vocab_size
103+ y = torch.randn(BATCH, SEQ_LEN, vocab, device=DEV)
104+ print(f"模型:DistilBertForMaskedLM 输入:input_ids{tuple(ids.shape)} "
105+ f"y: None -> Tensor{tuple(y.shape)}")
106+ 
107+ tag_b, target_b, rec_b, ms_b, _ = run(bert, ids, mask, y, False)
108+ tag_f, target_f, rec_f, ms_f, f_f = run(bert, ids, mask, y, True)
109+ 
110+ print("\n[1] 编译期针对变量 y 的 guard:")
111+ shown = [(t, n, ("value=None" if isnone else "value=非None")) for t, n, isnone in target_b]
112+ print(" ", shown if shown else "(未观察到)")
113+ 
114+ print("\n[2] y 从 None 变为 Tensor 后是否重编译:")
115+ print(f" {tag_b}:{'发生重编译 ❗' if rec_b else '未重编译 ✅'}")
116+ print(f" {tag_f}:{'发生重编译 ❗' if rec_f else '未重编译 ✅(仅放过 y=None 这一条)'}")
117+ print(" 结论:{}".format(
118+ "按变量名过滤成功消除重编译 🎉" if (rec_b and not rec_f) else "请检查"))
119+ 
120+ # 正确性:y=None 稳定签名下,Eager 与过滤后编译的 DistilBert logits 一致
121+ torch._dynamo.reset()
122+ cfc = torch.compile(f_f, backend="aot_eager",
123+ options={"guard_filter_fn": lambda es: [name_rule(e) for e in es]})
124+ with torch.no_grad():
125+ out_e = f_f(ids, mask)
126+ out_c = cfc(ids, mask)
127+ maxdiff = (out_e - out_c).abs().max().item()
128+ ok4 = torch.allclose(out_e, out_c, atol=1e-4)
129+ print(f"\n[3] 正确性(y=None 签名,DistilBert logits):")
130+ print(f" max|Δ| = {maxdiff:.3e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
131+ 
132+ print("\n[4] 性能:y=None -> y=Tensor 切换的当次调用耗时:")
133+ print(f" {tag_b}(含重编译):{ms_b:8.3f} ms")
134+ print(f" {tag_f}(缓存命中):{ms_f:8.3f} ms")
135+ print(f" 加速比 ≈ {ms_b / max(ms_f, 1e-6):8.2f}x")
136+ print("\n说明:按名过滤作用域最小,但仍属 unsafe——复用的是 y=None 分支图,")
137+ print(" 需确认 y 变为 Tensor 后的分支语义不需要重新特化。")
138+ 
139+ 
140+if __name__ == "__main__":
141+ main()
@@ -0,0 +1,146 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · Guard Filter · 示例5:过滤“全局变量”guard
4+================================================================
5+被测模型:distilbert-base-uncased(DistilBertForMaskedLM,离线 HF 缓存)
6+说明:本脚本没有任何自建网络模型——编译对象是一个普通 Python 函数,内部调用真实
7+ 的 DistilBertForMaskedLM,并在其 logits 上读取模块级全局配置做激活分支。
8+ 
9+当函数前向读取模块级全局对象(配置、特性开关、注册表等)时,Dynamo 会给这些
10+G['xxx'] 全局读取安装 is_global=True 的 guard(如 EQUALS_MATCH / TYPE_MATCH /
11+DICT 类 guard)。全局对象一旦被改动(哪怕只是值变化),默认就会重编译。
12+ 
13+对“整个进程生命周期内不希望其变化触发重编译”的全局对象,用
14+ [not entry.is_global for entry in entries]
15+把全部全局 guard 一次性过滤掉,只保留输入张量等局部 guard。
16+ 
17+运行:env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
18+ python 5_过滤全局变量guard.py
19+"""
20+import os
21+os.environ.setdefault("OMP_NUM_THREADS", "1")
22+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
23+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
24+ 
25+import time
26+import torch
27+import torch_npu # noqa: F401
28+from transformers import DistilBertForMaskedLM
29+ 
30+torch.npu.set_device(0)
31+DEV = "npu"
32+BATCH, SEQ_LEN = 2, 64
33+ 
34+# 模块级全局特性开关(对应工程里的 global config / feature flag)
35+GLOBAL_CONFIG = {"activation": "relu"}
36+ 
37+ 
38+def fwd_global(bert, input_ids, attention_mask):
39+ """以真实 DistilBert 为核心:logits 上的激活分支由模块级全局配置控制。"""
40+ logits = bert(input_ids=input_ids,
41+ attention_mask=attention_mask).logits
42+ if GLOBAL_CONFIG["activation"] == "relu": # 读取全局 -> is_global guard
43+ return torch.relu(logits)
44+ return torch.sigmoid(logits)
45+ 
46+ 
47+def make_filter(keep_rule, store):
48+ def guard_filter_fn(entries):
49+ store.clear()
50+ keeps = [bool(keep_rule(e)) for e in entries]
51+ store.extend([(str(e.guard_type), str(e.name), bool(e.is_global), k)
52+ for e, k in zip(entries, keeps)])
53+ return keeps
54+ return guard_filter_fn
55+ 
56+ 
57+def global_rule(e):
58+ return not e.is_global # 过滤掉所有全局 guard
59+ 
60+ 
61+def sync():
62+ torch.npu.synchronize()
63+ 
64+ 
65+def first_change_ms(compiled, bert, ids, mask):
66+ """状态切换后的“当次调用”耗时(baseline 当次含重编译,过滤后命中缓存)。"""
67+ sync()
68+ t0 = time.perf_counter()
69+ with torch.no_grad():
70+ compiled(bert, ids, mask)
71+ sync()
72+ return (time.perf_counter() - t0) * 1e3
73+ 
74+ 
75+def run(bert, ids, mask, use_filter):
76+ torch._dynamo.reset()
77+ GLOBAL_CONFIG["activation"] = "relu"
78+ store = []
79+ if use_filter:
80+ opt = {"guard_filter_fn": make_filter(global_rule, store)}
81+ tag = "过滤全局guard后"
82+ else:
83+ opt = {"guard_filter_fn": make_filter(lambda e: True, store)}
84+ tag = "不过滤"
85+ cf = torch.compile(fwd_global, backend="aot_eager", options=opt)
86+ with torch.no_grad():
87+ cf(bert, ids, mask) # 首次编译
88+ glob_guards = [f"{t}({n})" for t, n, g, k in store if g]
89+ GLOBAL_CONFIG["activation"] = "sigmoid" # 修改全局开关
90+ try:
91+ with torch.compiler.set_stance("fail_on_recompile"):
92+ cf(bert, ids, mask)
93+ rec = False
94+ except RuntimeError:
95+ rec = True
96+ ms = first_change_ms(cf, bert, ids, mask)
97+ return tag, glob_guards, rec, ms
98+ 
99+ 
100+def main():
101+ print("=" * 72)
102+ print("示例5:过滤全局变量 guard(基于 distilbert-base-uncased,is_global=True)")
103+ print("=" * 72)
104+ 
105+ bert = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
106+ bert.to(DEV).eval()
107+ ids = torch.randint(0, bert.config.vocab_size, (BATCH, SEQ_LEN)).npu()
108+ mask = torch.ones_like(ids).npu()
109+ print(f"被测模型:DistilBertForMaskedLM 输入:input_ids{tuple(ids.shape)}")
110+ 
111+ tag_b, gl_b, rec_b, ms_b = run(bert, ids, mask, False)
112+ tag_f, gl_f, rec_f, ms_f = run(bert, ids, mask, True)
113+ 
114+ print("\n[1] 编译期安装的全局 guard(is_global=True)示例:")
115+ print(" ", gl_b[:6], "..." if len(gl_b) > 6 else "")
116+ print(f" 共 {len(gl_b)} 条")
117+ 
118+ print("\n[2] 修改全局开关 activation: relu -> sigmoid 后是否重编译:")
119+ print(f" {tag_b}:{'发生重编译 ❗' if rec_b else '未重编译 ✅'}")
120+ print(f" {tag_f}:{'发生重编译 ❗' if rec_f else '未重编译 ✅(仅保留局部张量 guard)'}")
121+ print(" 结论:{}".format(
122+ "过滤全局 guard 成功消除重编译 🎉" if (rec_b and not rec_f) else "请检查"))
123+ 
124+ # 正确性:全局开关固定 relu 时,Eager 与过滤后编译的 DistilBert logits 一致
125+ torch._dynamo.reset()
126+ GLOBAL_CONFIG["activation"] = "relu"
127+ with torch.no_grad():
128+ cfc = torch.compile(fwd_global, backend="aot_eager",
129+ options={"guard_filter_fn": lambda es: [not e.is_global for e in es]})
130+ out_e = fwd_global(bert, ids, mask)
131+ out_c = cfc(bert, ids, mask)
132+ maxdiff = (out_e - out_c).abs().max().item()
133+ ok4 = torch.allclose(out_e, out_c, atol=1e-4)
134+ print(f"\n[3] 正确性(全局开关固定 relu,DistilBert logits):")
135+ print(f" max|Δ| = {maxdiff:.3e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
136+ 
137+ print("\n[4] 性能:全局开关变化后的当次调用耗时:")
138+ print(f" {tag_b}(含重编译):{ms_b:8.3f} ms")
139+ print(f" {tag_f}(缓存命中):{ms_f:8.3f} ms")
140+ print(f" 加速比 ≈ {ms_b / max(ms_f, 1e-6):8.2f}x")
141+ print("\n说明:官方提供同名内置 helper —— torch.compiler.skip_guard_on_globals_unsafe。")
142+ print(" 仅当你确信这些全局对象的变化不应影响已编译图时再使用。")
143+ 
144+ 
145+if __name__ == "__main__":
146+ main()
@@ -0,0 +1,176 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · Guard Filter · 示例6:结合官方内置 helper 过滤 nn.Module guard
4+================================================================
5+被测模型:distilbert-base-uncased(DistilBertForMaskedLM,离线 HF 缓存)
6+ 编译对象就是该模型实例本身,没有任何自建网络模型。
7+ DistilBert 内部含大量内建 nn.Linear / nn.Embedding,是观察模块 guard 的好载体。
8+torch.compiler 提供两个开箱即用的 guard_filter_fn:
9+ · skip_guard_on_inbuilt_nn_modules_unsafe:跳过 nn.Linear 等“内建模块”上的 guard
10+ · skip_guard_on_all_nn_modules_unsafe :跳过所有 nn.Module(含自定义模块)上的 guard
11+官方定位(docstring 原文):
12+ "the model code does not modify the nn module attributes. They can benefit
13+ from reduction in guard latency overhead using this API."
14+ 
15+为触发“内建子模块类型”guard,脚本只在验证环节把模型中某一个内建 nn.Linear
16+临时替换为下面定义的 ReplacementLinear——它不是模型,仅是与 nn.Linear 同语义
17+(含同形 weight/bias)但类型不同的替换层,用来检验 TYPE_MATCH 类 guard 是否被
18+helper 过滤。
19+ 
20+本脚本量化三件事:
21+ [1] guard 总数 / 保留数 / 过滤数(按类型统计,DistilBert 上约 1500 条 guard)
22+ [2] 重编译消除:把某个内建 nn.Linear 替换为“同语义但不同类型”的替换层后,
23+ baseline 会因子模块 TYPE_MATCH 失败而重编译;两个 helper 过滤掉该 guard,
24+ 直接复用已编译图(fail_on_recompile 姿态实测)。
25+ [3] 正确性:helper 只删 guard、不改数学,编译输出与 Eager 一致。
26+ [4] 性能:稳态缓存命中的端到端平均延迟(NPU 环境算子派发占主导,guard 收益
27+ 主要体现在 guard 数量上,深层网络 / CPU 派发占比高时收益更明显)。
28+ 
29+运行:env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
30+ python 6_结合内置helper.py
31+"""
32+import os
33+os.environ.setdefault("OMP_NUM_THREADS", "1")
34+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
35+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
36+ 
37+import collections
38+import time
39+import torch
40+import torch_npu # noqa: F401
41+import torch.nn as nn
42+from transformers import DistilBertForMaskedLM
43+ 
44+torch.npu.set_device(0)
45+DEV = "npu"
46+BATCH, SEQ_LEN = 2, 64
47+ 
48+HELPERS = [
49+ ("baseline(不过滤)", None),
50+ ("inbuilt(仅内建模块)", torch.compiler.skip_guard_on_inbuilt_nn_modules_unsafe),
51+ ("all(全部 nn.Module)", torch.compiler.skip_guard_on_all_nn_modules_unsafe),
52+]
53+ 
54+ 
55+class ReplacementLinear(nn.Linear):
56+ """同语义内建替换层(非模型,仅用于触发内建子模块 TYPE_MATCH guard)。"""
57+ pass
58+ 
59+ 
60+def sync():
61+ torch.npu.synchronize()
62+ 
63+ 
64+def make_inputs(bert):
65+ ids = torch.randint(0, bert.config.vocab_size, (BATCH, SEQ_LEN), device=DEV)
66+ return ids, torch.ones_like(ids)
67+ 
68+ 
69+def run(helper):
70+ """编译 DistilBert,统计 guard;随后换异型子模块做重编译判定;再测稳态耗时。"""
71+ torch._dynamo.reset()
72+ bert = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
73+ bert.to(DEV).eval()
74+ ids, mask = make_inputs(bert)
75+ 
76+ type_counter = collections.Counter()
77+ dropped_types = collections.Counter()
78+ 
79+ def flt(entries):
80+ keeps = [True] * len(entries) if helper is None else list(helper(entries))
81+ for e, k in zip(entries, keeps):
82+ t = str(e.guard_type)
83+ type_counter[t] += 1
84+ if not k:
85+ dropped_types[t] += 1
86+ return keeps
87+ 
88+ cf = torch.compile(bert, backend="aot_eager", options={"guard_filter_fn": flt})
89+ 
90+ def invoke():
91+ return cf(input_ids=ids, attention_mask=mask).logits
92+ 
93+ with torch.no_grad():
94+ for _ in range(5): # warmup,含首次编译(多段图的 guard 会在全部编译后出齐)
95+ invoke()
96+ # —— 重编译消除判定:把第 0 层 attention 的 q_lin 换成同语义异型 Linear ——
97+ attn = bert.distilbert.transformer.layer[0].attention
98+ old = attn.q_lin
99+ new_lin = ReplacementLinear(old.in_features, old.out_features, bias=True).to(DEV)
100+ new_lin.weight.data.copy_(old.weight.data)
101+ new_lin.bias.data.copy_(old.bias.data)
102+ attn.q_lin = new_lin
103+ try:
104+ with torch.compiler.set_stance("fail_on_recompile"):
105+ invoke()
106+ rec = False
107+ except RuntimeError:
108+ rec = True
109+ attn.q_lin = old # 还原,后续计时与正确性使用原模型
110+ # —— 稳态缓存命中计时 ——
111+ for _ in range(5):
112+ invoke()
113+ sync(); t0 = time.perf_counter()
114+ for _ in range(100):
115+ invoke()
116+ sync()
117+ ms = (time.perf_counter() - t0) / 100 * 1e3
118+ 
119+ total = sum(type_counter.values())
120+ dropped = sum(dropped_types.values())
121+ return total, total - dropped, dropped, dict(dropped_types), rec, ms, bert, ids, mask
122+ 
123+ 
124+def main():
125+ print("=" * 72)
126+ print("示例6:内置 helper 过滤 nn.Module guard(基于 distilbert-base-uncased)")
127+ print("=" * 72)
128+ 
129+ results = {}
130+ for name, helper in HELPERS:
131+ total, kept, dropped, dropped_types, rec, ms, bert, ids, mask = run(helper)
132+ results[name] = (total, kept, dropped, dropped_types, rec, ms, bert, ids, mask)
133+ print(f"\n[{name}]")
134+ print(f" guard 总数 = {total:4d},保留 = {kept:4d},过滤 = {dropped:4d}"
135+ f"({dropped / max(total,1) * 100:4.1f}%)")
136+ top = sorted(dropped_types.items(), key=lambda kv: -kv[1])[:4]
137+ if top:
138+ print(" 被过滤类型 TOP:", ", ".join(f"{t}×{n}" for t, n in top))
139+ print(f" 换异型子模块后重编译 = {rec}")
140+ print(f" 稳态缓存命中耗时 = {ms:8.3f} ms/次")
141+ 
142+ # [3] 正确性:以 all helper 为例,编译结果与 Eager 完全一致
143+ _, _, _, _, _, _, bert, ids, mask = results[HELPERS[2][0]]
144+ torch._dynamo.reset()
145+ with torch.no_grad():
146+ out_e = bert(input_ids=ids, attention_mask=mask).logits
147+ cf = torch.compile(
148+ bert, backend="aot_eager",
149+ options={"guard_filter_fn":
150+ torch.compiler.skip_guard_on_all_nn_modules_unsafe})
151+ out_c = cf(input_ids=ids, attention_mask=mask).logits
152+ maxdiff = (out_e - out_c).abs().max().item()
153+ ok4 = torch.allclose(out_e, out_c, atol=1e-4)
154+ print("\n[正确性] helper 仅删 guard、不改数学(DistilBert logits):")
155+ print(f" max|Δ| = {maxdiff:.3e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
156+ 
157+ tb = results[HELPERS[0][0]]
158+ ti = results[HELPERS[1][0]]
159+ ta = results[HELPERS[2][0]]
160+ print("\n[重编译消除] 内建 Linear 换成同语义异型子类后:")
161+ print(f" baseline 重编译 = {tb[4]}(TYPE_MATCH 守模块类型,必然重特化)")
162+ print(f" inbuilt 重编译 = {ti[4]}(内建模块 guard 已过滤,复用缓存)")
163+ print(f" all 重编译 = {ta[4]}(全部模块 guard 已过滤,复用缓存)")
164+ 
165+ print("\n[性能] 稳态缓存命中平均延迟:")
166+ print(f" baseline = {tb[5]:7.3f} ms/次")
167+ print(f" inbuilt = {ti[5]:7.3f} ms/次(guard {tb[0]} -> {ti[1]},减少 {ti[2]} 条)")
168+ print(f" all = {ta[5]:7.3f} ms/次(guard {tb[0]} -> {ta[1]},减少 {ta[2]} 条)")
169+ print(" 说明:guard 检查发生在算子派发前的 CPU 侧;本机 NPU 单算子派发开销较大,")
170+ print(" 端到端耗时主要由算子派发决定。深层/超深网络或 CPU 派发占比较高的场景下,")
171+ print(" guard 数量的大幅下降可直接转化为每次缓存命中的延迟收益。helper 属于 unsafe,")
172+ print(" 前提是运行期不更换这些 nn.Module 的类型/结构(权重数值作为图输入不受影响)。")
173+ 
174+ 
175+if __name__ == "__main__":
176+ main()
@@ -0,0 +1,107 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · Inductor + DVM(torch_npu 内置 dvm 后端)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+后端:torch.compile(..., backend="inductor", options={"npu_backend": "dvm"})
7+ DVM 能力由 torch_npu wheel 内置的 torch_npu._C.dvm 提供,无需额外安装。
8+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
9+ 
10+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8):
11+ env -u CI OMP_NUM_THREADS=1 TORCHINDUCTOR_COMPILE_THREADS=1 \
12+ TORCHINDUCTOR_NPU_BACKEND=dvm HF_HOME=/root/.cache/huggingface \
13+ python 1_改写脚本.py
14+说明:TORCHINDUCTOR_NPU_BACKEND=dvm 需在 import torch 前设置(脚本内已兜底)。
15+"""
16+import os
17+os.environ.setdefault("OMP_NUM_THREADS", "1")
18+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
19+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
20+os.environ.setdefault("TORCH_COMPILE_DEBUG", "0") # 不在脚本目录生成 torch_compile_debug
21+os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm" # 必须在 import torch 前指定
22+ 
23+import time
24+import torch
25+import torch_npu # noqa: F401 导入即注册 NPU 后端与内置 dvm
26+from transformers import DistilBertForMaskedLM
27+ 
28+BACKEND_NAME = "inductor + DVM"
29+BATCH, SEQ_LEN = 4, 128
30+WARMUP, ITERS = 3, 20
31+ 
32+ 
33+def sync():
34+ torch.npu.synchronize()
35+ 
36+ 
37+def bench(fn, warmup=WARMUP, iters=ITERS):
38+ with torch.no_grad():
39+ for _ in range(warmup):
40+ fn()
41+ sync()
42+ t0 = time.perf_counter()
43+ for _ in range(iters):
44+ fn()
45+ sync()
46+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
47+ 
48+ 
49+def main():
50+ torch.npu.set_device(0)
51+ print("=" * 72)
52+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
53+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
54+ f"device={torch.npu.get_device_name(0)}")
55+ print("TORCHINDUCTOR_NPU_BACKEND =", os.environ.get("TORCHINDUCTOR_NPU_BACKEND"))
56+ print("=" * 72)
57+ 
58+ # 1) 加载模型与输入 ----------------------------------------------------
59+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
60+ model.to("npu").eval()
61+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
62+ attention_mask = torch.ones_like(input_ids).npu()
63+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}")
64+ 
65+ # 2) Eager 基线 -------------------------------------------------------
66+ def run_eager():
67+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
68+ 
69+ t_eager = bench(run_eager)
70+ with torch.no_grad():
71+ out_eager = run_eager()
72+ 
73+ # 3) 编译后端(DVM)---------------------------------------------------
74+ t0 = time.perf_counter()
75+ compiled = torch.compile(
76+ model, backend="inductor", options={"npu_backend": "dvm"})
77+ with torch.no_grad():
78+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次触发 DVM 编译
79+ sync()
80+ t_compile = (time.perf_counter() - t0) * 1e3
81+ 
82+ def run_compiled():
83+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
84+ 
85+ t_compiled = bench(run_compiled)
86+ with torch.no_grad():
87+ out_compiled = run_compiled()
88+ 
89+ # 4) 正确性 -----------------------------------------------------------
90+ maxdiff = (out_eager - out_compiled).abs().max().item()
91+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
92+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
93+ speedup = t_eager / t_compiled
94+ print("\n[正确性] Eager vs 编译输出:")
95+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
96+ print(f" allclose(atol=1e-5) = {ok5}")
97+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
98+ print(f" 首次编译耗时 = {t_compile:9.1f} ms")
99+ print(f" Eager = {t_eager:9.3f} ms/次")
100+ print(f" {BACKEND_NAME:18s}= {t_compiled:9.3f} ms/次")
101+ print(f" 加速比 = {speedup:9.2f}x")
102+ print("\n结论:{},相对 Eager {:.2f}x。".format(
103+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
104+ 
105+ 
106+if __name__ == "__main__":
107+ main()
@@ -0,0 +1,103 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · Inductor + MLIR(torch-mlir 编译后端)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+后端:torch.compile(..., backend="inductor", options={"npu_backend": "mlir"})
7+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
8+ 
9+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8 + torch-mlir 0.0.1):
10+ env -u CI OMP_NUM_THREADS=1 TORCHINDUCTOR_COMPILE_THREADS=1 \
11+ HF_HOME=/root/.cache/huggingface python 1_改写脚本.py
12+说明:低内存机器务必 TORCHINDUCTOR_COMPILE_THREADS=1;env -u CI 关闭 CI 严格模式。
13+"""
14+import os
15+os.environ.setdefault("OMP_NUM_THREADS", "1")
16+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
17+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
18+os.environ.setdefault("TORCH_COMPILE_DEBUG", "0") # 不在脚本目录生成 torch_compile_debug
19+ 
20+import time
21+import torch
22+import torch_npu # noqa: F401 导入即注册 NPU 后端
23+from transformers import DistilBertForMaskedLM
24+ 
25+BACKEND_NAME = "inductor + MLIR"
26+BATCH, SEQ_LEN = 4, 128
27+WARMUP, ITERS = 3, 20
28+ 
29+ 
30+def sync():
31+ torch.npu.synchronize()
32+ 
33+ 
34+def bench(fn, warmup=WARMUP, iters=ITERS):
35+ with torch.no_grad():
36+ for _ in range(warmup):
37+ fn()
38+ sync()
39+ t0 = time.perf_counter()
40+ for _ in range(iters):
41+ fn()
42+ sync()
43+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
44+ 
45+ 
46+def main():
47+ torch.npu.set_device(0)
48+ print("=" * 72)
49+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
50+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
51+ f"device={torch.npu.get_device_name(0)}")
52+ print("=" * 72)
53+ 
54+ # 1) 加载模型与输入 ----------------------------------------------------
55+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
56+ model.to("npu").eval()
57+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
58+ attention_mask = torch.ones_like(input_ids).npu()
59+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}")
60+ 
61+ # 2) Eager 基线 -------------------------------------------------------
62+ def run_eager():
63+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
64+ 
65+ t_eager = bench(run_eager)
66+ with torch.no_grad():
67+ out_eager = run_eager()
68+ 
69+ # 3) 编译后端(MLIR)--------------------------------------------------
70+ t0 = time.perf_counter()
71+ compiled = torch.compile(
72+ model, backend="inductor", options={"npu_backend": "mlir"})
73+ with torch.no_grad():
74+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次触发 MLIR 编译
75+ sync()
76+ t_compile = (time.perf_counter() - t0) * 1e3
77+ 
78+ def run_compiled():
79+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
80+ 
81+ t_compiled = bench(run_compiled)
82+ with torch.no_grad():
83+ out_compiled = run_compiled()
84+ 
85+ # 4) 正确性 -----------------------------------------------------------
86+ maxdiff = (out_eager - out_compiled).abs().max().item()
87+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
88+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
89+ speedup = t_eager / t_compiled
90+ print("\n[正确性] Eager vs 编译输出:")
91+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
92+ print(f" allclose(atol=1e-5) = {ok5}")
93+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
94+ print(f" 首次编译耗时 = {t_compile:9.1f} ms")
95+ print(f" Eager = {t_eager:9.3f} ms/次")
96+ print(f" {BACKEND_NAME:18s}= {t_compiled:9.3f} ms/次")
97+ print(f" 加速比 = {speedup:9.2f}x")
98+ print("\n结论:{},相对 Eager {:.2f}x。".format(
99+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
100+ 
101+ 
102+if __name__ == "__main__":
103+ main()
@@ -0,0 +1,104 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · Inductor + Triton-Ascend(算子融合 + Triton NPU 内核)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+后端:torch.compile(..., backend="inductor"),NPU 内核走 triton-ascend。
7+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
8+ 
9+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8 + triton-ascend 3.2.2):
10+ env -u CI OMP_NUM_THREADS=1 TORCHINDUCTOR_COMPILE_THREADS=1 \
11+ HF_HOME=/root/.cache/huggingface python 1_改写脚本.py
12+说明:低内存机器务必 TORCHINDUCTOR_COMPILE_THREADS=1,否则编译大模型易 OOM;
13+ 必须 env -u CI 关闭 CI 严格模式,避免 make_fallback 断言中断。
14+"""
15+import os
16+os.environ.setdefault("OMP_NUM_THREADS", "1")
17+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
18+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
19+os.environ.setdefault("TORCH_COMPILE_DEBUG", "0") # 不在脚本目录生成 torch_compile_debug
20+os.environ.setdefault("TORCHINDUCTOR_NPU_BACKEND", "default") # inductor 默认 NPU 内核=Triton
21+ 
22+import time
23+import torch
24+import torch_npu # noqa: F401 导入即注册 NPU 后端
25+from transformers import DistilBertForMaskedLM
26+ 
27+BACKEND_NAME = "inductor + triton-ascend"
28+BATCH, SEQ_LEN = 4, 128
29+WARMUP, ITERS = 3, 20
30+ 
31+ 
32+def sync():
33+ torch.npu.synchronize()
34+ 
35+ 
36+def bench(fn, warmup=WARMUP, iters=ITERS):
37+ with torch.no_grad():
38+ for _ in range(warmup):
39+ fn()
40+ sync()
41+ t0 = time.perf_counter()
42+ for _ in range(iters):
43+ fn()
44+ sync()
45+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
46+ 
47+ 
48+def main():
49+ torch.npu.set_device(0)
50+ print("=" * 72)
51+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
52+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
53+ f"device={torch.npu.get_device_name(0)}")
54+ print("=" * 72)
55+ 
56+ # 1) 加载模型与输入 ----------------------------------------------------
57+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
58+ model.to("npu").eval()
59+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
60+ attention_mask = torch.ones_like(input_ids).npu()
61+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}")
62+ 
63+ # 2) Eager 基线 -------------------------------------------------------
64+ def run_eager():
65+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
66+ 
67+ t_eager = bench(run_eager)
68+ with torch.no_grad():
69+ out_eager = run_eager()
70+ 
71+ # 3) 编译后端 ---------------------------------------------------------
72+ t0 = time.perf_counter()
73+ compiled = torch.compile(model, backend="inductor")
74+ with torch.no_grad():
75+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次触发 Triton 编译
76+ sync()
77+ t_compile = (time.perf_counter() - t0) * 1e3
78+ 
79+ def run_compiled():
80+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
81+ 
82+ t_compiled = bench(run_compiled)
83+ with torch.no_grad():
84+ out_compiled = run_compiled()
85+ 
86+ # 4) 正确性 -----------------------------------------------------------
87+ maxdiff = (out_eager - out_compiled).abs().max().item()
88+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
89+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
90+ speedup = t_eager / t_compiled
91+ print("\n[正确性] Eager vs 编译输出:")
92+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
93+ print(f" allclose(atol=1e-5) = {ok5}")
94+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
95+ print(f" 首次编译耗时 = {t_compile:9.1f} ms")
96+ print(f" Eager = {t_eager:9.3f} ms/次")
97+ print(f" {BACKEND_NAME:18s}= {t_compiled:9.3f} ms/次")
98+ print(f" 加速比 = {speedup:9.2f}x")
99+ print("\n结论:{},相对 Eager {:.2f}x。".format(
100+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
101+ 
102+ 
103+if __name__ == "__main__":
104+ main()
@@ -0,0 +1,101 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · NPUGraph_EX(NPU Graph 增强模式)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+后端:torch.compile(..., backend="npugraph_ex")(torch_npu 提供,不依赖 torchair)
7+特点:NPUGraphs 的增强变体,对图捕获/重放路径做了进一步优化;同样要求静态输入,
8+ 首次调用包含图捕获开销,计时前需 warmup。
9+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
10+ 
11+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8):
12+ env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface python 1_改写脚本.py
13+"""
14+import os
15+os.environ.setdefault("OMP_NUM_THREADS", "1")
16+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
17+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
18+ 
19+import time
20+import torch
21+import torch_npu # noqa: F401 导入即注册 NPU 后端(含 npugraph_ex)
22+from transformers import DistilBertForMaskedLM
23+ 
24+BACKEND_NAME = "npugraph_ex"
25+BATCH, SEQ_LEN = 4, 128
26+WARMUP, ITERS = 5, 30 # 多给几次 warmup,确保首次图捕获完成后再计时
27+ 
28+ 
29+def sync():
30+ torch.npu.synchronize()
31+ 
32+ 
33+def bench(fn, warmup=WARMUP, iters=ITERS):
34+ with torch.no_grad():
35+ for _ in range(warmup):
36+ fn()
37+ sync()
38+ t0 = time.perf_counter()
39+ for _ in range(iters):
40+ fn()
41+ sync()
42+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
43+ 
44+ 
45+def main():
46+ torch.npu.set_device(0)
47+ print("=" * 72)
48+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
49+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
50+ f"device={torch.npu.get_device_name(0)}")
51+ print("=" * 72)
52+ 
53+ # 1) 加载模型与输入(要求固定形状/静态输入)------------------------------
54+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
55+ model.to("npu").eval()
56+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
57+ attention_mask = torch.ones_like(input_ids).npu()
58+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}(固定形状)")
59+ 
60+ # 2) Eager 基线 -------------------------------------------------------
61+ def run_eager():
62+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
63+ 
64+ t_eager = bench(run_eager)
65+ with torch.no_grad():
66+ out_eager = run_eager()
67+ 
68+ # 3) 编译后端(npugraph_ex,首次调用完成图捕获)------------------------
69+ t0 = time.perf_counter()
70+ compiled = torch.compile(model, backend="npugraph_ex")
71+ with torch.no_grad():
72+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次:图捕获
73+ sync()
74+ t_compile = (time.perf_counter() - t0) * 1e3
75+ 
76+ def run_compiled():
77+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
78+ 
79+ t_compiled = bench(run_compiled)
80+ with torch.no_grad():
81+ out_compiled = run_compiled()
82+ 
83+ # 4) 正确性 -----------------------------------------------------------
84+ maxdiff = (out_eager - out_compiled).abs().max().item()
85+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
86+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
87+ speedup = t_eager / t_compiled
88+ print("\n[正确性] Eager vs 编译输出:")
89+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
90+ print(f" allclose(atol=1e-5) = {ok5}")
91+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
92+ print(f" 首次图捕获耗时 = {t_compile:9.1f} ms")
93+ print(f" Eager = {t_eager:9.3f} ms/次")
94+ print(f" {BACKEND_NAME:14s}= {t_compiled:9.3f} ms/次(graph replay)")
95+ print(f" 加速比 = {speedup:9.2f}x")
96+ print("\n结论:{},相对 Eager {:.2f}x。".format(
97+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
98+ 
99+ 
100+if __name__ == "__main__":
101+ main()
@@ -0,0 +1,101 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · NPUGraphs(整图捕获为 NPU Graph 后重放)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+后端:torch.compile(..., backend="npugraphs")(torch_npu 提供,无需 torchair)
7+特点:把固定形状的计算序列捕获成单张 NPU Graph,之后以 graph replay 执行,
8+ 消除逐算子 launch/调度开销;首次调用包含图捕获开销,计时需 warmup。
9+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
10+ 
11+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8):
12+ env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface python 1_改写脚本.py
13+"""
14+import os
15+os.environ.setdefault("OMP_NUM_THREADS", "1")
16+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
17+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
18+ 
19+import time
20+import torch
21+import torch_npu # noqa: F401 导入即注册 NPU 后端(含 npugraphs)
22+from transformers import DistilBertForMaskedLM
23+ 
24+BACKEND_NAME = "npugraphs"
25+BATCH, SEQ_LEN = 4, 128
26+WARMUP, ITERS = 5, 30 # 多给几次 warmup,确保首次图捕获完成后再计时
27+ 
28+ 
29+def sync():
30+ torch.npu.synchronize()
31+ 
32+ 
33+def bench(fn, warmup=WARMUP, iters=ITERS):
34+ with torch.no_grad():
35+ for _ in range(warmup):
36+ fn()
37+ sync()
38+ t0 = time.perf_counter()
39+ for _ in range(iters):
40+ fn()
41+ sync()
42+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
43+ 
44+ 
45+def main():
46+ torch.npu.set_device(0)
47+ print("=" * 72)
48+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
49+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
50+ f"device={torch.npu.get_device_name(0)}")
51+ print("=" * 72)
52+ 
53+ # 1) 加载模型与输入(NPU Graph 要求固定形状/静态输入)---------------------
54+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
55+ model.to("npu").eval()
56+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
57+ attention_mask = torch.ones_like(input_ids).npu()
58+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}(固定形状)")
59+ 
60+ # 2) Eager 基线 -------------------------------------------------------
61+ def run_eager():
62+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
63+ 
64+ t_eager = bench(run_eager)
65+ with torch.no_grad():
66+ out_eager = run_eager()
67+ 
68+ # 3) 编译后端(npugraphs,首次调用完成图捕获)--------------------------
69+ t0 = time.perf_counter()
70+ compiled = torch.compile(model, backend="npugraphs")
71+ with torch.no_grad():
72+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次:图捕获
73+ sync()
74+ t_compile = (time.perf_counter() - t0) * 1e3
75+ 
76+ def run_compiled():
77+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
78+ 
79+ t_compiled = bench(run_compiled)
80+ with torch.no_grad():
81+ out_compiled = run_compiled()
82+ 
83+ # 4) 正确性 -----------------------------------------------------------
84+ maxdiff = (out_eager - out_compiled).abs().max().item()
85+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
86+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
87+ speedup = t_eager / t_compiled
88+ print("\n[正确性] Eager vs 编译输出:")
89+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
90+ print(f" allclose(atol=1e-5) = {ok5}")
91+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
92+ print(f" 首次图捕获耗时 = {t_compile:9.1f} ms")
93+ print(f" Eager = {t_eager:9.3f} ms/次")
94+ print(f" {BACKEND_NAME:14s}= {t_compiled:9.3f} ms/次(graph replay)")
95+ print(f" 加速比 = {speedup:9.2f}x")
96+ print("\n结论:{},相对 Eager {:.2f}x。".format(
97+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
98+ 
99+ 
100+if __name__ == "__main__":
101+ main()
@@ -0,0 +1,108 @@
1+# -*- coding: utf-8 -*-
2+"""
3+任务2 · 后端体验 · TorchAir-GE(TorchAir 图引擎 Graph Engine)
4+================================================================
5+模型:distilbert-base-uncased 的 DistilBertForMaskedLM(MLM 头,离线 HF 缓存)
6+后端:先 import torch_npu,再 import torchair;使用
7+ torchair.get_npu_backend(compiler_config=torchair.CompilerConfig())
8+ 交给 GE(Graph Engine)做整图编译与执行。
9+验证:编译后输出与 Eager 一致(allclose),并对比平均推理延迟。
10+ 
11+运行(910B,CANN 9.1.0,torch 2.7.1 + torch_npu 2.7.1.post8 + torchair 0.1):
12+ mkdir -p /root/.tvm_test_data /root/Ascend # GE 首次运行所需缓存目录
13+ env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface python 1_改写脚本.py
14+说明:GE 首次会做图编译,耗时明显长于普通后端,属正常现象。
15+"""
16+import os
17+os.environ.setdefault("OMP_NUM_THREADS", "1")
18+os.environ.setdefault("HF_HOME", "/root/.cache/huggingface")
19+os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
20+os.environ.setdefault("TORCH_COMPILE_DEBUG", "0") # 不在脚本目录生成 torch_compile_debug
21+os.chdir("/tmp") # GE/torchair 会在当前目录落 fusion_result.json 等产物,统一切到 /tmp 保持交付目录干净
22+ 
23+import time
24+import torch
25+import torch_npu # noqa: F401 必须先导入 torch_npu
26+import torchair # noqa: E402 再导入 torchair
27+from transformers import DistilBertForMaskedLM
28+ 
29+BACKEND_NAME = "TorchAir-GE"
30+BATCH, SEQ_LEN = 4, 128
31+WARMUP, ITERS = 3, 20
32+ 
33+ 
34+def sync():
35+ torch.npu.synchronize()
36+ 
37+ 
38+def bench(fn, warmup=WARMUP, iters=ITERS):
39+ with torch.no_grad():
40+ for _ in range(warmup):
41+ fn()
42+ sync()
43+ t0 = time.perf_counter()
44+ for _ in range(iters):
45+ fn()
46+ sync()
47+ return (time.perf_counter() - t0) / iters * 1e3 # ms/次
48+ 
49+ 
50+def main():
51+ torch.npu.set_device(0)
52+ print("=" * 72)
53+ print(f"torch.compile 后端体验:{BACKEND_NAME}")
54+ print(f"torch={torch.__version__} torch_npu={torch_npu.__version__} "
55+ f"torchair={getattr(torchair, '__version__', '0.1')} "
56+ f"device={torch.npu.get_device_name(0)}")
57+ print("=" * 72)
58+ 
59+ # 1) 加载模型与输入 ----------------------------------------------------
60+ model = DistilBertForMaskedLM.from_pretrained("distilbert-base-uncased")
61+ model.to("npu").eval()
62+ input_ids = torch.randint(0, model.config.vocab_size, (BATCH, SEQ_LEN)).npu()
63+ attention_mask = torch.ones_like(input_ids).npu()
64+ print(f"输入:input_ids{tuple(input_ids.shape)} attention_mask{tuple(attention_mask.shape)}")
65+ 
66+ # 2) Eager 基线 -------------------------------------------------------
67+ def run_eager():
68+ return model(input_ids=input_ids, attention_mask=attention_mask).logits
69+ 
70+ t_eager = bench(run_eager)
71+ with torch.no_grad():
72+ out_eager = run_eager()
73+ 
74+ # 3) 编译后端(TorchAir GE)-------------------------------------------
75+ t0 = time.perf_counter()
76+ ge_backend = torchair.get_npu_backend(compiler_config=torchair.CompilerConfig())
77+ compiled = torch.compile(model, backend=ge_backend)
78+ with torch.no_grad():
79+ compiled(input_ids=input_ids, attention_mask=attention_mask) # 首次:GE 图编译
80+ sync()
81+ t_compile = (time.perf_counter() - t0) * 1e3
82+ 
83+ def run_compiled():
84+ return compiled(input_ids=input_ids, attention_mask=attention_mask).logits
85+ 
86+ t_compiled = bench(run_compiled)
87+ with torch.no_grad():
88+ out_compiled = run_compiled()
89+ 
90+ # 4) 正确性 -----------------------------------------------------------
91+ maxdiff = (out_eager - out_compiled).abs().max().item()
92+ ok4 = torch.allclose(out_eager, out_compiled, atol=1e-4)
93+ ok5 = torch.allclose(out_eager, out_compiled, atol=1e-5)
94+ speedup = t_eager / t_compiled
95+ print("\n[正确性] Eager vs 编译输出:")
96+ print(f" max|Δ| = {maxdiff:.4e} allclose(atol=1e-4) = {ok4} {'✅' if ok4 else '❌'}")
97+ print(f" allclose(atol=1e-5) = {ok5}")
98+ print("\n[性能] 推理平均延迟(已 warmup,torch.npu.synchronize 计时):")
99+ print(f" 首次 GE 编译耗时 = {t_compile:9.1f} ms")
100+ print(f" Eager = {t_eager:9.3f} ms/次")
101+ print(f" {BACKEND_NAME:16s}= {t_compiled:9.3f} ms/次")
102+ print(f" 加速比 = {speedup:9.2f}x")
103+ print("\n结论:{},相对 Eager {:.2f}x。".format(
104+ "正确性校验通过" if ok4 else "正确性未通过,请检查", speedup))
105+ 
106+ 
107+if __name__ == "__main__":
108+ main()
@@ -0,0 +1,140 @@
1+=====================================================================
2+ 任务2 交付件存放指南
3+=====================================================================
4+ 
5+本指南详细说明任务2所有交付件的存放位置、文件命名和内容要求。
6+请严格按照本指南整理交付材料,确保验收顺利通过。
7+ 
8+ 
9+---------------------------------------------------------------------
10+一、文件夹结构
11+---------------------------------------------------------------------
12+ 
13+任务2-torch.compile体验与实践/
14+├── 交付件说明.txt ← 本文件(您正在阅读的)
15+├── 链接交付件.xls ← 用于填写 Issue 链接和PR链接
16+│
17+├── Guard Filter/
18+│ ├── 1_过滤字典版本guard.py
19+│ ├── 2_过滤运行时状态guard.py
20+│ ├── 3_组合多类guard过滤.py
21+│ ├── 4_按变量名过滤.py
22+│ ├── 5_过滤全局变量guard.py
23+│ ├── 6_结合内置helper.py
24+│ ├── 7_重编译消除验证截图.png ← 示例1(含模型名与三段证据)
25+│ ├── 8_正确性验证截图.png ← 示例1
26+│ ├── 9_性能对比截图.png ← 示例1
27+│ ├── 示例2_重编译消除验证截图.png / 示例2_正确性验证截图.png / 示例2_性能对比截图.png
28+│ ├── 示例3_重编译消除验证截图.png / 示例3_正确性验证截图.png / 示例3_性能对比截图.png
29+│ ├── 示例4_重编译消除验证截图.png / 示例4_正确性验证截图.png / 示例4_性能对比截图.png
30+│ ├── 示例5_重编译消除验证截图.png / 示例5_正确性验证截图.png / 示例5_性能对比截图.png
31+│ └── 示例6_重编译消除验证截图.png / 示例6_正确性验证截图.png / 示例6_性能对比截图.png
32+│ (6 个示例均基于真实 DistilBertForMaskedLM;每张整屏含 [2]重编译 [3]正确性 [4]性能)
33+│
34+├── Inductor-Triton/
35+│ ├── 1_改写脚本.py
36+│ ├── 2_正确性验证截图.png
37+│ └── 3_性能对比截图.png
38+│
39+├── Inductor-MLIR/
40+│ ├── 1_改写脚本.py
41+│ ├── 2_正确性验证截图.png
42+│ └── 3_性能对比截图.png
43+│
44+├── Inductor-DVM/
45+│ ├── 1_改写脚本.py
46+│ ├── 2_正确性验证截图.png
47+│ └── 3_性能对比截图.png
48+│
49+├── NPUGraphs/
50+│ ├── 1_改写脚本.py
51+│ ├── 2_正确性验证截图.png
52+│ └── 3_性能对比截图.png
53+│
54+├── NPUGraph_EX/
55+│ ├── 1_改写脚本.py
56+│ ├── 2_正确性验证截图.png
57+│ └── 3_性能对比截图.png
58+│
59+├── AOT_Eager/
60+│ ├── 1_改写脚本.py
61+│ ├── 2_正确性验证截图.png
62+│ └── 3_性能对比截图.png
63+│
64+└── TorchAir-GE/
65+ ├── 1_改写脚本.py
66+ ├── 2_正确性验证截图.png
67+ └── 3_性能对比截图.png
68+ 
69+*图片要求:截图清晰,能看到验证通过(未抛出重编译异常)
70+ 
71+---------------------------------------------------------------------
72+二、统一实验设置(910B3,CANN 9.1.0,Python 3.12/aarch64)
73+---------------------------------------------------------------------
74+ 模型:distilbert-base-uncased 的 DistilBertForMaskedLM(离线 HF 缓存,自包含)
75+ 输入:BATCH=4, SEQ_LEN=128;warmup=3,计时 20 次取均值;torch.npu.synchronize()
76+ 正确性判据:与 Eager 输出 allclose,atol=1e-4(实测最大绝对误差 3.8e-5~5.4e-5)
77+ Python 环境:7 个后端用 /root/venv271(torch 2.7.1 + torch_npu 2.7.1.post8
78+ + triton-ascend 3.2.2+release20260701 + torch-mlir + torchair);
79+ 6 个 Guard Filter 用 /root/.local/share/venv290(torch/torch_npu 2.9.0.post8)
80+ 通用前缀:env -u CI HF_HOME=/root/.cache/huggingface(inductor 再加
81+ OMP_NUM_THREADS=1 TORCHINDUCTOR_COMPILE_THREADS=1,低内存必须单线程编译)
82+ 
83+---------------------------------------------------------------------
84+三、本机实测结果(延迟为稳态平均,加速比相对同脚本 Eager)
85+---------------------------------------------------------------------
86+ 后端 最大|Δ| 后端延迟 Eager延迟 加速比 首次编译/图捕获
87+ AOT_Eager 4.10e-05 17.9 ms 5.6 ms 0.31x 无需编译
88+ NPUGraphs 4.15e-05 3.274 ms 5.5 ms 1.69x 图捕获约 4.36 s
89+ NPUGraph_EX 3.91e-05 3.000 ms 5.5 ms 2.01x 图捕获约 5.51 s
90+ TorchAir-GE 3.81e-05 2.289 ms 5.4 ms 2.34x GE 编译约 20.3 s
91+ Inductor-Triton 5.34e-05 7.646 ms 5.5 ms 0.68x 编译约 302 s
92+ Inductor-MLIR 5.34e-05 7.066 ms 5.474 ms 0.77x HFusion 编译约 1608 s(约27分)
93+ Inductor-DVM 5.34e-05 25.643 ms 5.570 ms 0.22x 编译约 17 s
94+ 说明:图类后端(NPU Graph / GE)在 batch=4、seq=128 小算子密集的推理上延迟收益
95+ 最明显;Inductor 三个后端首次编译成本高,在该小规模/小 batch 上稳态延迟暂
96+ 未超过 Eager,属首版在小模型上的真实表现,此处如实呈现,不做挑选。
97+ 
98+ Guard Filter(6 脚本,2.9.0,统一基于 distilbert-base-uncased / DistilBertForMaskedLM,
99+ batch=2、seq=64;编译对象均为该模型实例本身或内部调用该模型的普通 Python 函数,
100+ 6 个脚本中不含任何自建网络模型):
101+ 示例1 字典 guard 重编译❗→✅ max|Δ|=3.43e-05 切换当次 196.57x
102+ 示例2 运行时状态 Part B 重编译❗→✅ 2.69e-05 205.07x
103+ 示例3 组合(prefill/decode) ❗→✅ 3.78e-05 197.39x
104+ 示例4 按变量名 重编译❗→✅ 3.72e-05 574.26x
105+ 示例5 全局变量 重编译❗→✅ 2.58e-05 199.31x
106+ 示例6 内置 helper guard 1492 → inbuilt 832(过滤44.2%) / all 501(过滤66.4%);
107+ 内建 Linear 换同语义异型子类后 baseline 重编译=True、
108+ inbuilt/all=False;max|Δ|=3.62e-05;稳态 20.545→20.305 ms/次
109+ 说明:加速比为“状态切换当次调用”之比(baseline 当次含真实重编译约 3.6~11 s,
110+ 过滤后命中缓存约 19 ms);正确性均为 DistilBert logits 与 Eager allclose。
111+ 截图:7/8/9 为示例1 的重编译/正确性/性能三段;示例2~6 各附同名三张证据。
112+ 示例2 Part A 如实复现:GRAD_MODE 等 C++ 全局运行时态即使 filter 返回 False,
113+ 仍会被 AOTAutograd 按前/反向图重新特化(已作为 Issue #4996 反馈)。
114+ 
115+---------------------------------------------------------------------
116+四、逐脚本运行命令(截图时逐条执行)
117+---------------------------------------------------------------------
118+ # —— Guard Filter(venv290,进入 Guard Filter 目录)——
119+ for f in 1_过滤字典版本guard 2_过滤运行时状态guard 3_组合多类guard过滤 \
120+ 4_按变量名过滤 5_过滤全局变量guard 6_结合内置helper; do
121+ env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
122+ /root/.local/share/venv290/bin/python "$f.py"
123+ done
124+ 
125+ # —— 7 个后端(venv271,分别进入各自目录)——
126+ # AOT_Eager / NPUGraphs / NPUGraph_EX:
127+ env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
128+ /root/venv271/bin/python 1_改写脚本.py
129+ # TorchAir-GE(首次先建缓存目录):
130+ mkdir -p /root/.tvm_test_data /root/Ascend
131+ env -u CI OMP_NUM_THREADS=1 HF_HOME=/root/.cache/huggingface \
132+ /root/venv271/bin/python 1_改写脚本.py
133+ # Inductor-Triton:
134+ env -u CI OMP_NUM_THREADS=1 TORCHINDUCTOR_COMPILE_THREADS=1 \
135+ HF_HOME=/root/.cache/huggingface /root/venv271/bin/python 1_改写脚本.py
136+ # Inductor-MLIR:(同上,脚本内 options 指定 npu_backend=mlir)
137+ # Inductor-DVM :(同上,脚本内已在 import torch 前置 TORCHINDUCTOR_NPU_BACKEND=dvm)
138+ # 注意 MLIR 首次 HFusion 全图编译约 27 分钟,请耐心等待,属一次性成本。
139+=====================================================================
140+