已合并
add test/profiler #12588
huangyunlong创建于 2024年6月22日
add test/profiler #12588
已合并
从refs/pull/12588/head合入到master
共 5 个文件变更+2278-2
| @@ -0,0 +1,7 @@ | |||
| 1 | +# Python dependencies required for unit tests | ||
| 2 | + | ||
| 3 | +mypy==1.9.0 | ||
| 4 | +# Pin MyPy version because new errors are likely to appear with each release | ||
| 5 | +#Description: linter | ||
| 6 | +#Pinned versions: 1.9.0 | ||
| 7 | +#test that import: test_typing.py, test_type_hints.py | ||
| @@ -0,0 +1,736 @@ | |||
| 1 | +# Owner(s): ["oncall: profiler"] | ||
| 2 | + | ||
| 3 | +import functools | ||
| 4 | +import os | ||
| 5 | +import re | ||
| 6 | +import textwrap | ||
| 7 | +import traceback | ||
| 8 | +import unittest | ||
| 9 | + | ||
| 10 | +import expecttest | ||
| 11 | + | ||
| 12 | +import torch | ||
| 13 | +from torch._C._profiler import _ExtraFields_PyCall, _ExtraFields_PyCCall | ||
| 14 | +from torch.testing._internal.common_utils import ( | ||
| 15 | + IS_ARM64, | ||
| 16 | + IS_WINDOWS, | ||
| 17 | + run_tests, | ||
| 18 | + skipIfTorchDynamo, | ||
| 19 | + TEST_WITH_CROSSREF, | ||
| 20 | + TestCase, | ||
| 21 | +) | ||
| 22 | +from torch.utils._pytree import tree_map | ||
| 23 | + | ||
| 24 | +import torch_npu | ||
| 25 | +import torch_npu.testing | ||
| 26 | + | ||
| 27 | +# These functions can vary from based on platform and build (e.g. with NPU) | ||
| 28 | +# and generally distract from rather than adding to the test. | ||
| 29 | +PRUNE_ALL = 1 | ||
| 30 | +KEEP_ELLIPSES = 2 | ||
| 31 | +KEEP_NAME_AND_ELLIPSES = 3 | ||
| 32 | + | ||
| 33 | +PRUNE_FUNCTIONS = { | ||
| 34 | + "torch/utils/_pytree.py(...): tree_map": KEEP_NAME_AND_ELLIPSES, | ||
| 35 | + "torch/profiler/profiler.py(...): start": KEEP_ELLIPSES, | ||
| 36 | + "torch/profiler/profiler.py(...): stop_trace": KEEP_ELLIPSES, | ||
| 37 | + "torch/profiler/profiler.py(...): _transit_action": KEEP_ELLIPSES, | ||
| 38 | + "<built-in method __exit__ of torch._C.DisableTorchFunctionSubclass object at 0xXXXXXXXXXXXX>": PRUNE_ALL, | ||
| 39 | + "cudaStreamIsCapturing": PRUNE_ALL, | ||
| 40 | + # These show up only on NPU, prune them so the NPU and CPU expected results can be the same | ||
| 41 | + "cudaGetDeviceCount": PRUNE_ALL, | ||
| 42 | + "cudaGetDeviceProperties_v2": PRUNE_ALL, | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +class TorchFunctionTensor(torch.Tensor): | ||
| 47 | + | ||
| 48 | + def __torch_function__(cls, func, types, args=(), kwargs=None): | ||
| 49 | + return super().__torch_function__(func, types, args, kwargs) | ||
| 50 | + | ||
| 51 | + | ||
H | |||
| 52 | +class TorchDispatchTensor(torch.Tensor): | ||
| 53 | + | ||
| 54 | + def __new__(cls, elem): | ||
| 55 | + t = torch.Tensor._make_subclass(cls, elem, elem.requires_grad) | ||
| 56 | + t.elem = elem | ||
| 57 | + return t | ||
| 58 | + | ||
| 59 | + | ||
| 60 | + def __torch_dispatch__(cls, func, types, args=(), kwargs=None): | ||
| 61 | + def unwrap(x): | ||
| 62 | + return x.elem if isinstance(x, TorchDispatchTensor) else x | ||
| 63 | + | ||
| 64 | + def wrap(x): | ||
| 65 | + return TorchDispatchTensor(x) if isinstance(x, torch.Tensor) else x | ||
| 66 | + | ||
| 67 | + args = tree_map(unwrap, args) | ||
| 68 | + kwargs = tree_map(unwrap, kwargs or {}) | ||
| 69 | + | ||
| 70 | + return tree_map(wrap, func(*args, **kwargs)) | ||
| 71 | + | ||
| 72 | + | ||
| 73 | +class ProfilerTree: | ||
| 74 | + | ||
| 75 | + def test(f): | ||
| 76 | + """Mark unit test that will be using ProfilerTree to test traces. | ||
| 77 | + | ||
| 78 | + This decorator serves two purposes. First, it provides a method name | ||
| 79 | + that `format` can use to tell where the test runner (which is | ||
| 80 | + environment specific) ends and the unit test begins. Second, it runs | ||
| 81 | + the test with replicates and allows `assertTreesMatch` to adjust | ||
| 82 | + based on which replicate is running. | ||
| 83 | + """ | ||
| 84 | + | ||
| 85 | + | ||
| 86 | + def begin_unit_test_marker(self, replicates=3): | ||
| 87 | + try: | ||
| 88 | + for i in range(replicates): | ||
| 89 | + self.tree_replicate = i | ||
| 90 | + out = f(self) | ||
| 91 | + if self.tree_replicate is None: | ||
| 92 | + break | ||
| 93 | + return out | ||
| 94 | + finally: | ||
| 95 | + delattr(self, "tree_replicate") | ||
| 96 | + | ||
| 97 | + return begin_unit_test_marker | ||
| 98 | + | ||
| 99 | + | ||
| 100 | + def format(cls, profiler, indent: int = 0): | ||
| 101 | + def flatten(nodes, depth=0, out=None): | ||
| 102 | + if out is None: | ||
| 103 | + out = [] | ||
| 104 | + | ||
| 105 | + for node in nodes: | ||
| 106 | + cls.validate_node(node) | ||
| 107 | + name = cls.fmt_name(node.name) | ||
| 108 | + prune_level = PRUNE_FUNCTIONS.get(name.strip(), None) | ||
| 109 | + if prune_level is None: | ||
| 110 | + out.append((depth, name)) | ||
| 111 | + flatten(node.children, depth + 1, out) | ||
| 112 | + elif prune_level == KEEP_NAME_AND_ELLIPSES: | ||
| 113 | + out.append((depth, name)) | ||
| 114 | + if node.children: | ||
| 115 | + out.append((depth + 1, "...")) | ||
| 116 | + elif prune_level == KEEP_ELLIPSES: | ||
| 117 | + out.append((depth, "...")) | ||
| 118 | + else: | ||
| 119 | + assert prune_level == PRUNE_ALL | ||
| 120 | + | ||
| 121 | + return out | ||
| 122 | + | ||
| 123 | + flat_nodes = flatten(profiler.kineto_results.experimental_event_tree()) | ||
| 124 | + | ||
| 125 | + # Profiler inserts a `cudaDeviceSynchronize` at the end of profiling. | ||
| 126 | + # and may also insert 'Context Sync' NPU synchronization event. | ||
| 127 | + if flat_nodes and flat_nodes[-2][1] == "cudaDeviceSynchronize": | ||
| 128 | + flat_nodes = flat_nodes[:-2] | ||
| 129 | + | ||
| 130 | + if flat_nodes and flat_nodes[-1][1] == "cudaDeviceSynchronize": | ||
| 131 | + flat_nodes = flat_nodes[:-1] | ||
| 132 | + | ||
| 133 | + # Profiler inserts a `hipDeviceSynchronize` at the end of profiling. | ||
| 134 | + if flat_nodes and flat_nodes[-1][1] == "hipDeviceSynchronize": | ||
| 135 | + flat_nodes = flat_nodes[:-1] | ||
| 136 | + | ||
| 137 | + min_depth = min( | ||
| 138 | + [d + 1 for d, name in flat_nodes if "begin_unit_test_marker" in name] or [0] | ||
| 139 | + ) | ||
| 140 | + return textwrap.indent( | ||
| 141 | + "\n".join( | ||
| 142 | + [ | ||
| 143 | + f"{' ' * (d - min_depth)}{name.rstrip()}" | ||
| 144 | + for d, name in flat_nodes | ||
| 145 | + if d >= min_depth | ||
| 146 | + ] | ||
| 147 | + ), | ||
| 148 | + " " * indent, | ||
| 149 | + ) | ||
| 150 | + | ||
| 151 | + | ||
| 152 | + def fmt_name(name: str) -> str: | ||
| 153 | + match = re.match(r"^(.*)\.py\(([0-9]+)\): (.*)$", name) | ||
| 154 | + if match: | ||
| 155 | + filename, _, fn = match.groups() | ||
| 156 | + | ||
| 157 | + # This test can appear as `test/profiler/test_profiler_tree.py` | ||
| 158 | + # depending on where it is run from. | ||
| 159 | + test_file = os.path.splitext(os.path.split(__file__)[1])[0] | ||
| 160 | + if filename.endswith(test_file): | ||
| 161 | + filename = test_file | ||
| 162 | + | ||
| 163 | + # We test against a string literal, so all paths have to look like POSIX paths. | ||
| 164 | + filename = filename.replace(os.sep, "/") | ||
| 165 | + | ||
| 166 | + # We don't want to have to update this test every time PyTorch changes. | ||
| 167 | + # At some point we should test some line numbers, but for now it's | ||
| 168 | + # too brittle. | ||
| 169 | + lineno = "..." | ||
| 170 | + | ||
| 171 | + return f"{filename}.py({lineno}): {fn}" | ||
| 172 | + | ||
| 173 | + for kernel_pattern in ( | ||
| 174 | + "void at::native::elementwise_kernel", | ||
| 175 | + "void at::native::reduce_kernel", | ||
| 176 | + "void at::native::vectorized_elementwise_kernel", | ||
| 177 | + "void at::native::unrolled_elementwise_kernel", | ||
| 178 | + r"void [a-zA-Z0-9]+_kernel", # Nvidia kernels. | ||
| 179 | + ): | ||
| 180 | + name = re.sub( | ||
| 181 | + rf"{kernel_pattern}<.+>\(.+\)$", | ||
| 182 | + f"{kernel_pattern.replace('[a-zA-Z0-9]+', '...')}<...>(...)", | ||
| 183 | + name, | ||
| 184 | + ) | ||
| 185 | + | ||
| 186 | + return re.sub("object at 0x[0-9a-fA-F]+>", "object at 0xXXXXXXXXXXXX>", name) | ||
| 187 | + | ||
| 188 | + | ||
| 189 | + def validate_node(cls, node): | ||
| 190 | + extra_fields = node.extra_fields | ||
| 191 | + if isinstance(extra_fields, (_ExtraFields_PyCall, _ExtraFields_PyCCall)): | ||
| 192 | + # Check that the lineage established by the profiler matches the | ||
| 193 | + # caller recorded by the Python tracer. | ||
| 194 | + parent = node.parent | ||
| 195 | + while parent is not None: | ||
| 196 | + if isinstance(parent.extra_fields, _ExtraFields_PyCall): | ||
| 197 | + break | ||
| 198 | + parent = parent.parent | ||
| 199 | + | ||
| 200 | + def to_string(frame_state): | ||
| 201 | + return f"{frame_state.file_name}(...): {frame_state.function_name}" | ||
| 202 | + | ||
| 203 | + if parent: | ||
| 204 | + parent_name = to_string(parent.extra_fields.callsite) | ||
| 205 | + caller_name = to_string(extra_fields.caller) | ||
| 206 | + assert parent_name == caller_name, f"{parent_name} vs. {caller_name}" | ||
| 207 | + | ||
| 208 | + | ||
| 209 | + | ||
| 210 | +class TestProfilerTree(TestCase): | ||
| 211 | + def assertTreesMatch(self, actual: str, expected: str, allow_failure: bool = False): | ||
| 212 | + # Warning: Here be dragons | ||
| 213 | + # Different platforms will have subtly different behavior for Python | ||
| 214 | + # tracing. Observed differences include: | ||
| 215 | + # 1) Windows symbolicates names differently from posix | ||
| 216 | + # 2) The profile callback for c_call does not fire for Tensor.__pow__ | ||
| 217 | + # on certain platforms. This is not caused by the function tracer, | ||
| 218 | + # but by cPython itself. | ||
| 219 | + # | ||
| 220 | + # The purpose of these unit tests is to ensure that the profiler is | ||
| 221 | + # doing reasonable things. When these platform dependent variations occur | ||
| 222 | + # simply coerce them into a platform independent form. If you made a | ||
| 223 | + # change in the codebase which changes the trace produced, simply use | ||
| 224 | + # EXPECTTEST_ACCEPT=1 to update the tests to reflect the new structure. | ||
| 225 | + | ||
| 226 | + # expecttest will not show the diff view if `len(actual) < len(expected)` | ||
| 227 | + if not expecttest.ACCEPT: | ||
| 228 | + actual = actual.ljust(len(expected)) | ||
| 229 | + self.maxDiff = None | ||
| 230 | + | ||
| 231 | + replicate = getattr(self, "tree_replicate", None) | ||
| 232 | + self.assertIsNotNone( | ||
| 233 | + replicate, "Please annotate test with `@ProfilerTree.test`" | ||
| 234 | + ) | ||
| 235 | + | ||
| 236 | + # The profiler should produce deterministic results and should return | ||
| 237 | + # to a clean state after each run. As a result, only the first | ||
| 238 | + # replicate is allowed to update `expected`. If subsequent runs do not | ||
| 239 | + # match it is a bug in the profiler. | ||
| 240 | + if replicate: | ||
| 241 | + self.assertEqual(actual, expected) | ||
| 242 | + else: | ||
| 243 | + try: | ||
| 244 | + self.assertExpectedInline(actual, expected, skip=1) | ||
| 245 | + except AssertionError as e: | ||
| 246 | + if allow_failure: | ||
| 247 | + self.tree_replicate = None | ||
| 248 | + msg = traceback.format_exception_only(type(e), e)[0] | ||
| 249 | + print(msg.split("AssertionError:")[-1]) | ||
| 250 | + else: | ||
| 251 | + raise | ||
| 252 | + | ||
| 253 | + | ||
| 254 | + | ||
| 255 | + def test_profiler_experimental_tree(self): | ||
| 256 | + t1, t2 = torch.ones(1, requires_grad=True), torch.ones(1, requires_grad=True) | ||
| 257 | + with torch.profiler.profile() as p: | ||
| 258 | + z = torch.add(t1, t2) | ||
| 259 | + y = torch.ones(1) | ||
| 260 | + loss = (y - z) ** 2 | ||
| 261 | + loss.backward() | ||
| 262 | + | ||
| 263 | + self.assertTreesMatch( | ||
| 264 | + ProfilerTree.format(p.profiler, 12), | ||
| 265 | + """\ | ||
| 266 | + aten::add | ||
| 267 | + aten::ones | ||
| 268 | + aten::empty | ||
| 269 | + aten::fill_ | ||
| 270 | + aten::sub | ||
| 271 | + aten::pow | ||
| 272 | + aten::result_type | ||
| 273 | + aten::to | ||
| 274 | + aten::ones_like | ||
| 275 | + aten::empty_like | ||
| 276 | + aten::empty_strided | ||
| 277 | + aten::fill_ | ||
| 278 | + autograd::engine::evaluate_function: PowBackward0 | ||
| 279 | + PowBackward0 | ||
| 280 | + aten::pow | ||
| 281 | + aten::result_type | ||
| 282 | + aten::to | ||
| 283 | + aten::copy_ | ||
| 284 | + aten::mul | ||
| 285 | + aten::mul | ||
| 286 | + aten::to | ||
| 287 | + aten::_to_copy | ||
| 288 | + aten::empty_strided | ||
| 289 | + aten::copy_ | ||
| 290 | + aten::mul | ||
| 291 | + autograd::engine::evaluate_function: SubBackward0 | ||
| 292 | + SubBackward0 | ||
| 293 | + aten::neg | ||
| 294 | + autograd::engine::evaluate_function: AddBackward0 | ||
| 295 | + AddBackward0 | ||
| 296 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 297 | + torch::autograd::AccumulateGrad | ||
| 298 | + aten::new_empty_strided | ||
| 299 | + aten::empty_strided | ||
| 300 | + aten::copy_ | ||
| 301 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 302 | + torch::autograd::AccumulateGrad | ||
| 303 | + aten::detach | ||
| 304 | + detach""", | ||
| 305 | + ) | ||
| 306 | + | ||
| 307 | + | ||
| 308 | + | ||
| 309 | + def test_profiler_experimental_tree_with_record_function(self): | ||
| 310 | + with torch.profiler.profile() as p: | ||
| 311 | + with torch.autograd.profiler.record_function("Top level Annotation"): | ||
| 312 | + with torch.autograd.profiler.record_function("First Annotation"): | ||
| 313 | + x = torch.ones((1,), requires_grad=True) | ||
| 314 | + | ||
| 315 | + # Check that we correctly handle the case when a user | ||
| 316 | + # annotation does not call `__exit__`. | ||
| 317 | + _ = torch.autograd.profiler.record_function( | ||
| 318 | + "Second Annotation" | ||
| 319 | + ).__enter__() | ||
| 320 | + | ||
| 321 | + y = x + 1 | ||
| 322 | + with torch.autograd.profiler.record_function("Third Annotation"): | ||
| 323 | + y.backward() | ||
| 324 | + | ||
| 325 | + # NB: The `aten::zeros` before the record function annotations are due to | ||
| 326 | + # `at::cpp_custom_type_hack`. When we switch to `torch::CustomClassHolder` | ||
| 327 | + # they will disappear. | ||
| 328 | + self.assertTreesMatch( | ||
| 329 | + ProfilerTree.format(p.profiler, 12), | ||
| 330 | + """\ | ||
| 331 | + Top level Annotation | ||
| 332 | + First Annotation | ||
| 333 | + aten::ones | ||
| 334 | + aten::empty | ||
| 335 | + aten::fill_ | ||
| 336 | + Second Annotation | ||
| 337 | + aten::add | ||
| 338 | + aten::to | ||
| 339 | + aten::_to_copy | ||
| 340 | + aten::empty_strided | ||
| 341 | + aten::copy_ | ||
| 342 | + Third Annotation | ||
| 343 | + aten::ones_like | ||
| 344 | + aten::empty_like | ||
| 345 | + aten::empty_strided | ||
| 346 | + aten::fill_ | ||
| 347 | + autograd::engine::evaluate_function: AddBackward0 | ||
| 348 | + AddBackward0 | ||
| 349 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 350 | + torch::autograd::AccumulateGrad | ||
| 351 | + aten::new_empty_strided | ||
| 352 | + aten::empty_strided | ||
| 353 | + aten::copy_""", | ||
| 354 | + ) | ||
| 355 | + | ||
| 356 | + | ||
| 357 | + | ||
| 358 | + def test_profiler_experimental_tree_with_memory(self): | ||
| 359 | + t1, t2 = torch.ones(1, requires_grad=True), torch.ones(1, requires_grad=True) | ||
| 360 | + with torch.profiler.profile(profile_memory=True) as p: | ||
| 361 | + z = torch.add(t1, t2) | ||
| 362 | + y = torch.ones(1) | ||
| 363 | + loss = (y - z) ** 2 | ||
| 364 | + loss.backward() | ||
| 365 | + | ||
| 366 | + self.assertTreesMatch( | ||
| 367 | + ProfilerTree.format(p.profiler, 12), | ||
| 368 | + """\ | ||
| 369 | + aten::add | ||
| 370 | + [memory] | ||
| 371 | + aten::ones | ||
| 372 | + aten::empty | ||
| 373 | + [memory] | ||
| 374 | + aten::fill_ | ||
| 375 | + aten::sub | ||
| 376 | + [memory] | ||
| 377 | + aten::pow | ||
| 378 | + aten::result_type | ||
| 379 | + aten::to | ||
| 380 | + [memory] | ||
| 381 | + aten::ones_like | ||
| 382 | + aten::empty_like | ||
| 383 | + aten::empty_strided | ||
| 384 | + [memory] | ||
| 385 | + aten::fill_ | ||
| 386 | + autograd::engine::evaluate_function: PowBackward0 | ||
| 387 | + PowBackward0 | ||
| 388 | + aten::pow | ||
| 389 | + aten::result_type | ||
| 390 | + aten::to | ||
| 391 | + [memory] | ||
| 392 | + aten::copy_ | ||
| 393 | + aten::mul | ||
| 394 | + [memory] | ||
| 395 | + aten::mul | ||
| 396 | + aten::to | ||
| 397 | + aten::_to_copy | ||
| 398 | + aten::empty_strided | ||
| 399 | + [memory] | ||
| 400 | + aten::copy_ | ||
| 401 | + [memory] | ||
| 402 | + [memory] | ||
| 403 | + [memory] | ||
| 404 | + aten::mul | ||
| 405 | + [memory] | ||
| 406 | + [memory] | ||
| 407 | + [memory] | ||
| 408 | + [memory] | ||
| 409 | + autograd::engine::evaluate_function: SubBackward0 | ||
| 410 | + SubBackward0 | ||
| 411 | + aten::neg | ||
| 412 | + [memory] | ||
| 413 | + [memory] | ||
| 414 | + autograd::engine::evaluate_function: AddBackward0 | ||
| 415 | + AddBackward0 | ||
| 416 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 417 | + torch::autograd::AccumulateGrad | ||
| 418 | + aten::new_empty_strided | ||
| 419 | + aten::empty_strided | ||
| 420 | + [memory] | ||
| 421 | + aten::copy_ | ||
| 422 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 423 | + torch::autograd::AccumulateGrad | ||
| 424 | + aten::detach | ||
| 425 | + detach | ||
| 426 | + [memory]""", | ||
| 427 | + ) | ||
| 428 | + | ||
| 429 | + | ||
| 430 | + TEST_WITH_CROSSREF, "crossref intercepts calls and changes the callsite." | ||
| 431 | + ) | ||
| 432 | + | ||
| 433 | + def test_profiler_experimental_tree_with_memory_and_stack(self): | ||
| 434 | + t1, t2 = torch.ones(1, requires_grad=True), torch.ones(1, requires_grad=True) | ||
| 435 | + with torch.profiler.profile(with_stack=True, profile_memory=True) as p: | ||
| 436 | + z = torch.add(t1, t2) | ||
| 437 | + y = torch.ones(1) | ||
| 438 | + loss = torch.pow(y - z, 2) | ||
| 439 | + loss.backward() | ||
| 440 | + | ||
| 441 | + self.assertTreesMatch( | ||
| 442 | + ProfilerTree.format(p.profiler, 12), | ||
| 443 | + """\ | ||
| 444 | + test_profiler_tree.py(...): test_profiler_experimental_tree_with_memory_and_stack | ||
| 445 | + torch/profiler/profiler.py(...): __enter__ | ||
| 446 | + ... | ||
| 447 | + <built-in method add of type object at 0xXXXXXXXXXXXX> | ||
| 448 | + aten::add | ||
| 449 | + [memory] | ||
| 450 | + <built-in method ones of type object at 0xXXXXXXXXXXXX> | ||
| 451 | + aten::ones | ||
| 452 | + aten::empty | ||
| 453 | + [memory] | ||
| 454 | + aten::fill_ | ||
| 455 | + aten::sub | ||
| 456 | + [memory] | ||
| 457 | + <built-in method pow of type object at 0xXXXXXXXXXXXX> | ||
| 458 | + aten::pow | ||
| 459 | + aten::result_type | ||
| 460 | + aten::to | ||
| 461 | + [memory] | ||
| 462 | + torch/_tensor.py(...): backward | ||
| 463 | + <built-in function _has_torch_function_unary> | ||
| 464 | + torch/autograd/__init__.py(...): backward | ||
| 465 | + <built-in method _are_functorch_transforms_active of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 466 | + <built-in function isinstance> | ||
| 467 | + <built-in function isinstance> | ||
| 468 | + <built-in function len> | ||
| 469 | + torch/autograd/__init__.py(...): _tensor_or_tensors_to_tuple | ||
| 470 | + torch/autograd/__init__.py(...): _make_grads | ||
| 471 | + <built-in function isinstance> | ||
| 472 | + <built-in method numel of Tensor object at 0xXXXXXXXXXXXX> | ||
| 473 | + <built-in method ones_like of type object at 0xXXXXXXXXXXXX> | ||
| 474 | + aten::ones_like | ||
| 475 | + aten::empty_like | ||
| 476 | + aten::empty_strided | ||
| 477 | + [memory] | ||
| 478 | + aten::fill_ | ||
| 479 | + <built-in method append of list object at 0xXXXXXXXXXXXX> | ||
| 480 | + torch/autograd/graph.py(...): _engine_run_backward | ||
| 481 | + logging/__init__.py(...): getEffectiveLevel | ||
| 482 | + <built-in method run_backward of torch._C._EngineBase object at 0xXXXXXXXXXXXX> | ||
| 483 | + autograd::engine::evaluate_function: PowBackward0 | ||
| 484 | + PowBackward0 | ||
| 485 | + aten::pow | ||
| 486 | + aten::result_type | ||
| 487 | + aten::to | ||
| 488 | + [memory] | ||
| 489 | + aten::copy_ | ||
| 490 | + aten::mul | ||
| 491 | + [memory] | ||
| 492 | + aten::mul | ||
| 493 | + aten::to | ||
| 494 | + aten::_to_copy | ||
| 495 | + aten::empty_strided | ||
| 496 | + [memory] | ||
| 497 | + aten::copy_ | ||
| 498 | + [memory] | ||
| 499 | + [memory] | ||
| 500 | + [memory] | ||
| 501 | + aten::mul | ||
| 502 | + [memory] | ||
| 503 | + [memory] | ||
| 504 | + [memory] | ||
| 505 | + [memory] | ||
| 506 | + autograd::engine::evaluate_function: SubBackward0 | ||
| 507 | + SubBackward0 | ||
| 508 | + aten::neg | ||
| 509 | + [memory] | ||
| 510 | + [memory] | ||
| 511 | + autograd::engine::evaluate_function: AddBackward0 | ||
| 512 | + AddBackward0 | ||
| 513 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 514 | + torch::autograd::AccumulateGrad | ||
| 515 | + aten::new_empty_strided | ||
| 516 | + aten::empty_strided | ||
| 517 | + [memory] | ||
| 518 | + aten::copy_ | ||
| 519 | + autograd::engine::evaluate_function: torch::autograd::AccumulateGrad | ||
| 520 | + torch::autograd::AccumulateGrad | ||
| 521 | + aten::detach | ||
| 522 | + detach | ||
| 523 | + [memory] | ||
| 524 | + torch/profiler/profiler.py(...): __exit__ | ||
| 525 | + torch/profiler/profiler.py(...): stop | ||
| 526 | + ...""", | ||
| 527 | + ) | ||
| 528 | + | ||
| 529 | + | ||
| 530 | + | ||
| 531 | + TEST_WITH_CROSSREF, "crossref intercepts calls and changes the callsite." | ||
| 532 | + ) | ||
| 533 | + | ||
| 534 | + def test_profiler_experimental_tree_with_stack_and_modules(self): | ||
| 535 | + class MyModule(torch.nn.Module): | ||
| 536 | + def __init__(self): | ||
| 537 | + super().__init__() | ||
| 538 | + self.layers = [ | ||
| 539 | + torch.nn.ReLU(), | ||
| 540 | + torch.nn.Linear(1, 1), | ||
| 541 | + torch.nn.ReLU(), | ||
| 542 | + ] | ||
| 543 | + | ||
| 544 | + def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| 545 | + for l in self.layers: | ||
| 546 | + x = l(x) | ||
| 547 | + return x | ||
| 548 | + | ||
| 549 | + model = MyModule() | ||
| 550 | + with torch.profiler.profile(with_stack=True) as p: | ||
| 551 | + for _ in range(2): | ||
| 552 | + model(torch.ones((1,))) | ||
| 553 | + self.maxDiff = None | ||
| 554 | + self.assertTreesMatch( | ||
| 555 | + ProfilerTree.format(p.profiler, 12), | ||
| 556 | + """\ | ||
| 557 | + test_profiler_tree.py(...): test_profiler_experimental_tree_with_stack_and_modules | ||
| 558 | + torch/profiler/profiler.py(...): __enter__ | ||
| 559 | + ... | ||
| 560 | + <built-in method ones of type object at 0xXXXXXXXXXXXX> | ||
| 561 | + aten::ones | ||
| 562 | + aten::empty | ||
| 563 | + aten::fill_ | ||
| 564 | + nn.Module: MyModule_0 | ||
| 565 | + torch/nn/modules/module.py(...): _call_impl | ||
| 566 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 567 | + test_profiler_tree.py(...): forward | ||
| 568 | + nn.Module: ReLU_0 | ||
| 569 | + torch/nn/modules/module.py(...): _call_impl | ||
| 570 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 571 | + torch/nn/modules/activation.py(...): forward | ||
| 572 | + torch/nn/functional.py(...): relu | ||
| 573 | + <built-in function _has_torch_function_unary> | ||
| 574 | + <built-in method relu of type object at 0xXXXXXXXXXXXX> | ||
| 575 | + aten::relu | ||
| 576 | + aten::clamp_min | ||
| 577 | + nn.Module: Linear_0 | ||
| 578 | + torch/nn/modules/module.py(...): _call_impl | ||
| 579 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 580 | + torch/nn/modules/linear.py(...): forward | ||
| 581 | + torch/nn/modules/module.py(...): __getattr__ | ||
| 582 | + torch/nn/modules/module.py(...): __getattr__ | ||
| 583 | + <built-in function linear> | ||
| 584 | + aten::linear | ||
| 585 | + aten::reshape | ||
| 586 | + aten::view | ||
| 587 | + aten::t | ||
| 588 | + aten::transpose | ||
| 589 | + aten::as_strided | ||
| 590 | + aten::addmm | ||
| 591 | + aten::expand | ||
| 592 | + aten::as_strided | ||
| 593 | + aten::copy_ | ||
| 594 | + aten::resolve_conj | ||
| 595 | + aten::resolve_conj | ||
| 596 | + aten::resolve_conj | ||
| 597 | + aten::view | ||
| 598 | + nn.Module: ReLU_1 | ||
| 599 | + torch/nn/modules/module.py(...): _call_impl | ||
| 600 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 601 | + torch/nn/modules/activation.py(...): forward | ||
| 602 | + torch/nn/functional.py(...): relu | ||
| 603 | + <built-in function _has_torch_function_unary> | ||
| 604 | + <built-in method relu of type object at 0xXXXXXXXXXXXX> | ||
| 605 | + aten::relu | ||
| 606 | + aten::clamp_min | ||
| 607 | + <built-in method ones of type object at 0xXXXXXXXXXXXX> | ||
| 608 | + aten::ones | ||
| 609 | + aten::empty | ||
| 610 | + aten::fill_ | ||
| 611 | + nn.Module: MyModule_0 | ||
| 612 | + torch/nn/modules/module.py(...): _call_impl | ||
| 613 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 614 | + test_profiler_tree.py(...): forward | ||
| 615 | + nn.Module: ReLU_0 | ||
| 616 | + torch/nn/modules/module.py(...): _call_impl | ||
| 617 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 618 | + torch/nn/modules/activation.py(...): forward | ||
| 619 | + torch/nn/functional.py(...): relu | ||
| 620 | + <built-in function _has_torch_function_unary> | ||
| 621 | + <built-in method relu of type object at 0xXXXXXXXXXXXX> | ||
| 622 | + aten::relu | ||
| 623 | + aten::clamp_min | ||
| 624 | + nn.Module: Linear_0 | ||
| 625 | + torch/nn/modules/module.py(...): _call_impl | ||
| 626 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 627 | + torch/nn/modules/linear.py(...): forward | ||
| 628 | + torch/nn/modules/module.py(...): __getattr__ | ||
| 629 | + torch/nn/modules/module.py(...): __getattr__ | ||
| 630 | + <built-in function linear> | ||
| 631 | + aten::linear | ||
| 632 | + aten::reshape | ||
| 633 | + aten::view | ||
| 634 | + aten::t | ||
| 635 | + aten::transpose | ||
| 636 | + aten::as_strided | ||
| 637 | + aten::addmm | ||
| 638 | + aten::expand | ||
| 639 | + aten::as_strided | ||
| 640 | + aten::copy_ | ||
| 641 | + aten::resolve_conj | ||
| 642 | + aten::resolve_conj | ||
| 643 | + aten::resolve_conj | ||
| 644 | + aten::view | ||
| 645 | + nn.Module: ReLU_1 | ||
| 646 | + torch/nn/modules/module.py(...): _call_impl | ||
| 647 | + <built-in method _get_tracing_state of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 648 | + torch/nn/modules/activation.py(...): forward | ||
| 649 | + torch/nn/functional.py(...): relu | ||
| 650 | + <built-in function _has_torch_function_unary> | ||
| 651 | + <built-in method relu of type object at 0xXXXXXXXXXXXX> | ||
| 652 | + aten::relu | ||
| 653 | + aten::clamp_min | ||
| 654 | + torch/profiler/profiler.py(...): __exit__ | ||
| 655 | + torch/profiler/profiler.py(...): stop | ||
| 656 | + ...""", | ||
| 657 | + ) | ||
| 658 | + | ||
| 659 | + | ||
| 660 | + TEST_WITH_CROSSREF, "crossref intercepts calls and changes the callsite." | ||
| 661 | + ) | ||
| 662 | + | ||
| 663 | + def test_profiler_experimental_tree_with_stack_and_torch_function(self): | ||
| 664 | + x = TorchFunctionTensor(torch.ones((1,))) | ||
| 665 | + y = torch.ones((1,)) | ||
| 666 | + | ||
| 667 | + # There's some lazy initialization in __torch_function__. If we don't | ||
| 668 | + # run this the first run won't match the replicates. | ||
| 669 | + torch.add(x, y) | ||
| 670 | + | ||
| 671 | + with torch.profiler.profile(with_stack=True) as p: | ||
| 672 | + torch.add(x, y) | ||
| 673 | + | ||
| 674 | + self.assertTreesMatch( | ||
| 675 | + ProfilerTree.format(p.profiler, 12), | ||
| 676 | + """\ | ||
| 677 | + test_profiler_tree.py(...): test_profiler_experimental_tree_with_stack_and_torch_function | ||
| 678 | + torch/profiler/profiler.py(...): __enter__ | ||
| 679 | + ... | ||
| 680 | + <built-in method add of type object at 0xXXXXXXXXXXXX> | ||
| 681 | + test_profiler_tree.py(...): __torch_function__ | ||
| 682 | + torch/_tensor.py(...): __torch_function__ | ||
| 683 | + <built-in function all> | ||
| 684 | + torch/_tensor.py(...): <genexpr> | ||
| 685 | + <built-in function issubclass> | ||
| 686 | + torch/_tensor.py(...): <genexpr> | ||
| 687 | + <built-in method add of type object at 0xXXXXXXXXXXXX> | ||
| 688 | + aten::add | ||
| 689 | + torch/_tensor.py(...): _convert | ||
| 690 | + <built-in function isinstance> | ||
| 691 | + <built-in function isinstance> | ||
| 692 | + <built-in method as_subclass of Tensor object at 0xXXXXXXXXXXXX> | ||
| 693 | + aten::alias | ||
| 694 | + <built-in function isinstance> | ||
| 695 | + torch/profiler/profiler.py(...): __exit__ | ||
| 696 | + torch/profiler/profiler.py(...): stop | ||
| 697 | + ...""", | ||
| 698 | + ) | ||
| 699 | + | ||
| 700 | + | ||
| 701 | + TEST_WITH_CROSSREF, "crossref intercepts calls and changes the callsite." | ||
| 702 | + ) | ||
| 703 | + | ||
| 704 | + def test_profiler_experimental_tree_with_stack_and_torch_dispatch(self): | ||
| 705 | + x = TorchDispatchTensor(torch.ones((1,))) | ||
| 706 | + y = torch.ones((1,)) | ||
| 707 | + | ||
| 708 | + with torch.profiler.profile(with_stack=True) as p: | ||
| 709 | + x + y | ||
| 710 | + | ||
| 711 | + self.assertTreesMatch( | ||
| 712 | + ProfilerTree.format(p.profiler, 12), | ||
| 713 | + """\ | ||
| 714 | + test_profiler_tree.py(...): test_profiler_experimental_tree_with_stack_and_torch_dispatch | ||
| 715 | + torch/profiler/profiler.py(...): __enter__ | ||
| 716 | + ... | ||
| 717 | + aten::add | ||
| 718 | + test_profiler_tree.py(...): __torch_dispatch__ | ||
| 719 | + torch/utils/_pytree.py(...): tree_map | ||
| 720 | + ... | ||
| 721 | + torch/utils/_pytree.py(...): tree_map | ||
| 722 | + ... | ||
| 723 | + torch/_ops.py(...): __call__ | ||
| 724 | + <built-in method of PyCapsule object at 0xXXXXXXXXXXXX> | ||
| 725 | + aten::add | ||
| 726 | + torch/utils/_pytree.py(...): tree_map | ||
| 727 | + ... | ||
| 728 | + torch/profiler/profiler.py(...): __exit__ | ||
| 729 | + torch/profiler/profiler.py(...): stop | ||
| 730 | + ...""", | ||
| 731 | + ) | ||
| 732 | + | ||
| 733 | + | ||
| 734 | +if __name__ == "__main__": | ||
| 735 | + run_tests() | ||
| 736 | + | ||
| @@ -121,4 +121,8 @@ test/test_jit_llga_fuser.py | |||
| 121 | test/test_type_hints.py | 121 | test/test_type_hints.py |
| 122 | test/test_typing.py | 122 | test/test_typing.py |
| 123 | mypy.ini | 123 | mypy.ini |
| 124 | -.ci/docker/requirements-ci.txt | 124 | +test/profiler/profiler_utils_mock_events.json |
| 125 | +test/profiler/test_execution_trace.py | ||
| 126 | +test/profiler/test_profiler.py | ||
| 127 | +test/profiler/test_record_function.py | ||
| 128 | +test/profiler/test_torch_tidy.py | ||
| @@ -31405,5 +31405,14 @@ | |||
| 31405 | "test_sparse_zeros_trunc_npu_int32 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], | 31405 | "test_sparse_zeros_trunc_npu_int32 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], |
| 31406 | "test_sparse_zeros_trunc_npu_int64 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], | 31406 | "test_sparse_zeros_trunc_npu_int64 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], |
| 31407 | "test_sparse_zeros_trunc_npu_int8 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], | 31407 | "test_sparse_zeros_trunc_npu_int8 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], |
| 31408 | - "test_sparse_zeros_trunc_npu_uint8 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]] | 31408 | + "test_sparse_zeros_trunc_npu_uint8 (__main__.TestSparseUnaryUfuncsPRIVATEUSE1)": ["", [""]], |
| 31409 | + "test_memory_timeline_no_id (__main__.TestMemoryProfilerE2E)": ["", [""]], | ||
| 31410 | + "test_extract_gradients_from_optimizer_set_to_none (__main__.TestIdentifyGradients)": ["", [""]], | ||
| 31411 | + "test_fuzz_symbolize (__main__.TestExperimentalUtils)": ["", [""]], | ||
| 31412 | + "test_profiler_strides (__main__.TestProfiler)": ["", [""]], | ||
| 31413 | + "test_schedule_function_count (__main__.TestProfiler)": ["", [""]], | ||
| 31414 | + "test_profiler_experimental_tree_with_memory_and_stack (__main__.TestProfilerTree)": ["", [""]], | ||
| 31415 | + "test_profiler_experimental_tree_with_stack_and_modules (__main__.TestProfilerTree)": ["", [""]], | ||
| 31416 | + "test_profiler_experimental_tree_with_stack_and_torch_dispatch (__main__.TestProfilerTree)": ["", [""]], | ||
| 31417 | + "test_profiler_experimental_tree_with_stack_and_torch_function (__main__.TestProfilerTree)": ["", [""]] | ||
| 31409 | } | 31418 | } |
已删除