已合并
test: add Ascend NPU UT for torch.autograd.profiler.parse_nvprof_trace #42230
yuhongming-2026创建于 7月20日
test: add Ascend NPU UT for torch.autograd.profiler.parse_nvprof_trace #42230
已合并
yuhongming-2026创建于 7月20日
1 个文件变更+123-0
@@ -0,0 +1,123 @@
1+# Copyright (c) 2026 Huawei Technologies Co., Ltd
2+# All rights reserved.
3+#
4+# Licensed under the BSD 3-Clause License (the "License");
5+# you may not use this file except in compliance with the License.
6+# You may obtain a copy of the License at
7+#
8+# https://opensource.org/licenses/BSD-3-Clause
9+#
10+# Unless required by applicable law or agreed to in writing, software
11+# distributed under the License is distributed on an "AS IS" BASIS,
12+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+# See the License for the specific language governing permissions and
14+# limitations under the License.
15+# Owner(s): ["oncall: profiler"]
16+"""Ascend NPU adaptation checks for torch.autograd.profiler.parse_nvprof_trace.
17+ 
18+``torch.autograd.profiler.parse_nvprof_trace`` / ``load_nvprof`` parse NVIDIA
19+nvprof CUPTI SQLite. Ascend does not emit that format; NPU profiling uses
20+``torch.autograd.profiler.profile(use_device="npu")``.
21+ 
22+Community pytorch has limited CUPTI-oriented coverage and does not provide an
23+Ascend/NPU adaptation suite for this API. This file therefore:
24+ 1. checks the APIs remain available under the torch_npu stack;
25+ 2. verifies NPU profiler semantics with real NPU ops;
26+ 3. exercises ``EnforceUnique`` (used inside ``parse_nvprof_trace``);
27+ 4. confirms ``parse_nvprof_trace`` / ``load_nvprof`` still reject missing
28+ nvprof DB paths after NPU profiling (negative path; no CUPTI fixture).
29+"""
30+ 
31+import os
32+import tempfile
33+ 
34+import torch
35+from torch.autograd.profiler import EnforceUnique, load_nvprof, parse_nvprof_trace
36+from torch.testing._internal.common_utils import TestCase, run_tests
37+ 
38+ 

四个测试 PR 没有验证 NPU profiler 语义,构造的输入仍是 CUDA nvprof/CUPTI 格式。

likedislike
yuhongming-2026
yuhongming-2026
29 天前 评论:
39+class TestParseNvprofTraceAscendNPU(TestCase):
40+ """NPU checks for parse_nvprof_trace availability and NPU profiler semantics."""
41+ 
42+ def setUp(self):
43+ if not hasattr(torch, "npu") or not torch.npu.is_available():
44+ self.skipTest("Requires Ascend NPU (CANN). Skip on CPU/CUDA-only hosts.")
45+ torch.npu.set_device(0)
46+ self.device = torch.device("npu:0")
47+ 
48+ def _mm_on_npu(self):
49+ a = torch.randn(8, 8, device=self.device)
50+ b = torch.randn(8, 8, device=self.device)
51+ return torch.mm(a, b)
52+ 
53+ def _event_names(self, prof):
54+ return [e.name for e in prof.function_events]
55+ 
56+ def test_api_available_on_npu_stack(self):
57+ self.assertTrue(hasattr(torch.autograd.profiler, "parse_nvprof_trace"))
58+ self.assertTrue(callable(torch.autograd.profiler.parse_nvprof_trace))
59+ self.assertTrue(hasattr(torch.autograd.profiler, "load_nvprof"))
60+ self.assertTrue(callable(torch.autograd.profiler.load_nvprof))
61+ self.assertIs(parse_nvprof_trace, torch.autograd.profiler.parse_nvprof_trace)
62+ self.assertIs(load_nvprof, torch.autograd.profiler.load_nvprof)
63+ 
64+ def test_npu_profiler_records_mm(self):
65+ """Real NPU profiler path: use_device='npu' must capture mm events."""
66+ with torch.autograd.profiler.profile(use_device="npu") as prof:
67+ out = self._mm_on_npu()
68+ torch.npu.synchronize()
69+ self.assertEqual(out.device.type, "npu")
70+ self.assertTrue(
71+ any("mm" in name for name in self._event_names(prof)),
72+ f"expected mm in NPU profiler events, got {self._event_names(prof)}",
73+ )
74+ 
75+ def test_npu_profiler_records_add_and_mm(self):
76+ """NPU profiler should record multiple ops executed on device."""
77+ with torch.autograd.profiler.profile(use_device="npu") as prof:
78+ a = torch.randn(16, 16, device=self.device)
79+ b = torch.randn(16, 16, device=self.device)
80+ c = torch.mm(a, b)
81+ d = c + a
82+ torch.npu.synchronize()
83+ self.assertEqual(d.device.type, "npu")
84+ names = self._event_names(prof)
85+ self.assertTrue(any("mm" in n for n in names), f"missing mm in {names}")
86+ self.assertTrue(
87+ any(("add" in n) or ("+" in n) for n in names),
88+ f"missing add in {names}",
89+ )
90+ 
91+ def test_npu_profiler_event_timing(self):
92+ """Captured NPU profiler events should expose non-negative CPU time."""
93+ with torch.autograd.profiler.profile(use_device="npu") as prof:
94+ _ = self._mm_on_npu()
95+ torch.npu.synchronize()
96+ mm_events = [e for e in prof.function_events if "mm" in e.name]
97+ self.assertGreater(len(mm_events), 0)
98+ for evt in mm_events:
99+ self.assertGreaterEqual(evt.cpu_time, 0.0)
100+ 
101+ def test_parse_apis_reject_missing_nvprof_db_after_npu_profile(self):
102+ """After NPU profiling, parse APIs remain callable and reject missing DB."""
103+ with torch.autograd.profiler.profile(use_device="npu") as prof:
104+ _ = self._mm_on_npu()
105+ torch.npu.synchronize()
106+ self.assertGreater(len(list(prof.function_events)), 0)
107+ 
108+ missing = os.path.join(tempfile.gettempdir(), "missing_nvprof_ascend.sqlite")
109+ with self.assertRaises(Exception):
110+ parse_nvprof_trace(missing)
111+ with self.assertRaises(Exception):
112+ load_nvprof(missing)
113+ 
114+ def test_enforce_unique_on_npu(self):
115+ _ = torch.randn(2, 2, device=self.device)
116+ unique = EnforceUnique()
117+ unique.see("a", 1)
118+ with self.assertRaises(RuntimeError):
119+ unique.see("a", 1)
120+ 
121+ 
122+if __name__ == "__main__":
123+ run_tests()