已合并
feat(_inductor): Support DVM for the r2.12 branch #39779
SorryNaCN创建于 7月1日
feat(_inductor): Support DVM for the r2.12 branch #39779
已合并
共 33 个文件变更+4323-1463
| @@ -21,6 +21,10 @@ | |||
| 21 | path = third_party/torch-mlir | 21 | path = third_party/torch-mlir |
| 22 | url = https://gitcode.com/gh_mirrors/to/torch-mlir.git | 22 | url = https://gitcode.com/gh_mirrors/to/torch-mlir.git |
| 23 | update = none | 23 | update = none |
| 24 | +[submodule "third_party/dvm/dvm"] | ||
| 25 | + path = third_party/dvm/dvm | ||
| 26 | + url = https://gitcode.com/mindspore/dvm.git | ||
| 27 | + branch = r2.10 | ||
| 24 | [submodule "third_party/acl_src/runtime"] | 28 | [submodule "third_party/acl_src/runtime"] |
| 25 | path = third_party/acl_src/runtime | 29 | path = third_party/acl_src/runtime |
| 26 | url = https://gitcode.com/cann/runtime.git | 30 | url = https://gitcode.com/cann/runtime.git |
| @@ -321,6 +321,10 @@ endif() | |||
| 321 | 321 | ||
| 322 | add_subdirectory(${TORCHNPU_THIRD_PARTY_ROOT}/fmt EXCLUDE_FROM_ALL) | 322 | add_subdirectory(${TORCHNPU_THIRD_PARTY_ROOT}/fmt EXCLUDE_FROM_ALL) |
| 323 | 323 | ||
| 324 | +add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/dvm) | ||
| 325 | +add_dependencies(${PLUGIN_NAME} dvm_build) | ||
| 326 | +target_link_libraries(${PLUGIN_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/third_party/dvm/dvm/libdvm.a) | ||
| 327 | + | ||
| 324 | link_directories(${PYTORCH_INSTALL_DIR}/lib) | 328 | link_directories(${PYTORCH_INSTALL_DIR}/lib) |
| 325 | link_directories(${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs) | 329 | link_directories(${TORCHNPU_THIRD_PARTY_ROOT}/acl/libs) |
| 326 | 330 | ||
| @@ -0,0 +1,52 @@ | |||
| 1 | +# Owner(s): ["module: tests"] | ||
| 2 | +"""End-to-end: elu/elu_backward are numerically correct through the DVM | ||
| 3 | +backend's native aclnn fallback (elu is excluded from decomposition; the policy | ||
| 4 | +assertions live in test_dvm_decomp.py). | ||
| 5 | + | ||
| 6 | +The DVM backend is pinned via torch.compile(options={"npu_backend": "dvm"}). | ||
| 7 | +We deliberately do NOT import torch_npu._inductor at module scope: importing it | ||
| 8 | +loads a backend at import time, which would turn the first torch.compile into a | ||
| 9 | +mid-process backend switch (default -> dvm). Plain ``import torch_npu`` does not | ||
| 10 | +load _inductor. | ||
| 11 | +""" | ||
| 12 | +import unittest | ||
| 13 | + | ||
| 14 | +import torch | ||
| 15 | +import torch_npu | ||
| 16 | +from torch.testing._internal.common_utils import ( | ||
| 17 | + instantiate_parametrized_tests, | ||
| 18 | + parametrize, | ||
| 19 | + run_tests, | ||
| 20 | + TestCase, | ||
| 21 | +) | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +class TestDvmEluNative(TestCase): | ||
| 26 | + | ||
| 27 | + def test_elu_forward_backward_matches_eager(self, dtype): | ||
| 28 | + tol = 1e-3 if dtype == torch.float32 else 4e-3 | ||
| 29 | + # build in fp32 then cast (npu normal kernel has no bf16 support) | ||
| 30 | + ref = torch.randn(256, 4096, device="npu").to(dtype) | ||
| 31 | + x_e = ref.detach().clone().requires_grad_(True) | ||
| 32 | + x_c = ref.detach().clone().requires_grad_(True) | ||
| 33 | + | ||
| 34 | + def fn(t): | ||
| 35 | + return torch.nn.functional.elu(t) | ||
| 36 | + | ||
| 37 | + out_e = fn(x_e) | ||
| 38 | + out_e.float().sum().backward() | ||
| 39 | + | ||
| 40 | + compiled = torch.compile( | ||
| 41 | + fn, backend="inductor", options={"npu_backend": "dvm"} | ||
| 42 | + ) | ||
| 43 | + out_c = compiled(x_c) | ||
| 44 | + out_c.float().sum().backward() | ||
| 45 | + | ||
| 46 | + self.assertEqual(out_e, out_c, atol=tol, rtol=tol) | ||
| 47 | + self.assertEqual(x_e.grad, x_c.grad, atol=tol, rtol=tol) | ||
| 48 | + | ||
| 49 | + | ||
| 50 | +instantiate_parametrized_tests(TestDvmEluNative) | ||
| 51 | +if __name__ == "__main__": | ||
| 52 | + run_tests() | ||
| @@ -0,0 +1,175 @@ | |||
| 1 | +from unittest import mock | ||
| 2 | + | ||
| 3 | +import torch | ||
| 4 | + | ||
| 5 | +from torch.testing._internal.common_utils import TestCase | ||
| 6 | +from torch.testing._internal.common_utils import ( | ||
| 7 | + run_tests, | ||
| 8 | + parametrize, | ||
| 9 | + instantiate_parametrized_tests, | ||
| 10 | +) | ||
| 11 | +from torch_npu._inductor.dvm.graph_fusion import ( | ||
| 12 | + DvmGraphFusionPatch, | ||
| 13 | + _dvm_generate_fallback_kernel, | ||
| 14 | + _fused_metas, | ||
| 15 | +) | ||
| 16 | +from torch_npu._inductor.dvm.graph_build import is_fx_dynamic | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +class TestModule(torch.nn.Module): | ||
| 20 | + def __init__(self, *args, **kwargs): | ||
| 21 | + super().__init__(*args, **kwargs) | ||
| 22 | + | ||
| 23 | + def forward(self, a, b, c): | ||
| 24 | + add = a + b | ||
| 25 | + mul = add * c | ||
| 26 | + return torch.sum(mul, dim=(0,), keepdim=True) + 1 | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +class MatMulModule(torch.nn.Module): | ||
| 30 | + def __init__(self, *args, **kwargs): | ||
| 31 | + super().__init__(*args, **kwargs) | ||
| 32 | + | ||
| 33 | + def forward(self, a, b): | ||
| 34 | + mm = torch.mm(a.t(), b) | ||
| 35 | + mm = mm.to(torch.float32) | ||
| 36 | + return mm + 1 | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +class TestDvmByGraphFusion(TestCase): | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + def test_basic_partitioning(self, dtype, is_dynamic): | ||
| 43 | + a = torch.normal(0, 0.1, size=(512, 1024), dtype=dtype).npu() | ||
| 44 | + b = torch.normal(0, 0.1, size=(512, 1024), dtype=torch.float16).npu() | ||
| 45 | + c = torch.normal(0, 0.1, size=(1, 1024), dtype=dtype).npu() | ||
| 46 | + model = TestModule() | ||
| 47 | + | ||
| 48 | + with DvmGraphFusionPatch(): | ||
| 49 | + dvm_compiled_model = torch.compile( | ||
| 50 | + model, backend="inductor", dynamic=is_dynamic | ||
| 51 | + ) | ||
| 52 | + with torch.no_grad(): | ||
| 53 | + expect = model(a, b, c) | ||
| 54 | + result = dvm_compiled_model(a, b, c) | ||
| 55 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 56 | + | ||
| 57 | + | ||
| 58 | + | ||
| 59 | + def test_basic_partitioning_npugraph(self, dtype, is_dynamic): | ||
| 60 | + a = torch.normal(0, 0.1, size=(512, 1024), dtype=dtype).npu() | ||
| 61 | + b = torch.normal(0, 0.1, size=(512, 1024), dtype=torch.float16).npu() | ||
| 62 | + c = torch.normal(0, 0.1, size=(1, 1024), dtype=dtype).npu() | ||
| 63 | + model = TestModule() | ||
| 64 | + with DvmGraphFusionPatch(): | ||
| 65 | + dvm_compiled_model = torch.compile( | ||
| 66 | + model, | ||
| 67 | + backend="inductor", | ||
| 68 | + dynamic=is_dynamic, | ||
| 69 | + options={"triton.cudagraphs": True}, | ||
| 70 | + ) | ||
| 71 | + with torch.no_grad(): | ||
| 72 | + expect = model(a, b, c) | ||
| 73 | + result = dvm_compiled_model(a, b, c) | ||
| 74 | + result = dvm_compiled_model(a, b, c) | ||
| 75 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 76 | + | ||
| 77 | + | ||
| 78 | + | ||
| 79 | + | ||
| 80 | + def test_matmul(self, k, n, m, dtype, is_dynamic): | ||
| 81 | + a = torch.normal(0, 0.02, size=(k, n), dtype=dtype).npu() | ||
| 82 | + b = torch.normal(0, 0.02, size=(k, m), dtype=dtype).npu() | ||
| 83 | + model = MatMulModule() | ||
| 84 | + | ||
| 85 | + with DvmGraphFusionPatch(): | ||
| 86 | + dvm_compiled_model = torch.compile( | ||
| 87 | + model, backend="inductor", dynamic=is_dynamic | ||
| 88 | + ) | ||
| 89 | + with torch.no_grad(): | ||
| 90 | + expect = model(a, b) | ||
| 91 | + result = dvm_compiled_model(a, b) | ||
| 92 | + self.assertEqual(expect, result, atol=2e-3, rtol=2e-3) | ||
| 93 | + | ||
| 94 | + | ||
| 95 | +instantiate_parametrized_tests(TestDvmByGraphFusion) | ||
| 96 | + | ||
| 97 | + | ||
| 98 | +class _AddModule(torch.nn.Module): | ||
| 99 | + def forward(self, x): | ||
| 100 | + return x + 1 | ||
| 101 | + | ||
| 102 | + | ||
| 103 | +class TestDvmFallbackStridePatchGuard(TestCase): | ||
| 104 | + """Guard adb97b9: skip stride patch in dynamic fused subgraph codegen.""" | ||
| 105 | + | ||
| 106 | + def _make_fallback_kernel(self, fused_id=0): | ||
| 107 | + fallback_kernel = mock.MagicMock() | ||
| 108 | + fallback_kernel.op_overload._name = "dvm::fused_graph_0" | ||
| 109 | + fallback_kernel.codegen_args.return_value = ["buf0", fused_id] | ||
| 110 | + fallback_kernel.codegen_kwargs.return_value = [] | ||
| 111 | + fallback_kernel.get_name.return_value = "buf_out" | ||
| 112 | + return fallback_kernel | ||
| 113 | + | ||
| 114 | + def _make_codegen_wrapper(self): | ||
| 115 | + wrapper = mock.MagicMock() | ||
| 116 | + wrapper.header = mock.MagicMock() | ||
| 117 | + return wrapper | ||
| 118 | + | ||
| 119 | + def _make_fused_meta(self): | ||
| 120 | + meta = mock.MagicMock() | ||
| 121 | + meta.gm = mock.MagicMock() | ||
| 122 | + meta.name = "dvm_graph_fused_0" | ||
| 123 | + codegen = mock.MagicMock() | ||
| 124 | + codegen.cont_flag_input = [True] | ||
| 125 | + codegen.need_trans_input = [False] | ||
| 126 | + meta.codegen.return_value = (codegen, "# dvm kernel\n") | ||
| 127 | + return meta | ||
| 128 | + | ||
| 129 | + | ||
| 130 | + "torch_npu._inductor.dvm.graph_fusion.patch_gm_placeholder_strides_from_codegen_args" | ||
| 131 | + ) | ||
| 132 | + def test_fallback_kernel_stride_patch_guarded_by_is_fx_dynamic(self, mock_patch): | ||
| 133 | + gm_static = torch.fx.symbolic_trace(_AddModule()) | ||
| 134 | + placeholder = next(n for n in gm_static.graph.nodes if n.op == "placeholder") | ||
| 135 | + placeholder.meta["val"] = torch.randn(2, 3) | ||
| 136 | + self.assertFalse(is_fx_dynamic(gm_static)) | ||
| 137 | + | ||
| 138 | + batch = torch.export.Dim("batch", min=1, max=1024) | ||
| 139 | + exported = torch.export.export( | ||
| 140 | + _AddModule(), | ||
| 141 | + (torch.randn(2, 3),), | ||
| 142 | + dynamic_shapes={"x": {0: batch}}, | ||
| 143 | + ) | ||
| 144 | + self.assertTrue(is_fx_dynamic(exported.graph_module)) | ||
| 145 | + | ||
| 146 | + meta = self._make_fused_meta() | ||
| 147 | + try: | ||
| 148 | + _fused_metas[0] = meta | ||
| 149 | + with mock.patch( | ||
| 150 | + "torch_npu._inductor.dvm.graph_fusion.is_fx_dynamic", return_value=True | ||
| 151 | + ): | ||
| 152 | + _dvm_generate_fallback_kernel( | ||
| 153 | + self._make_codegen_wrapper(), | ||
| 154 | + self._make_fallback_kernel(), | ||
| 155 | + ) | ||
| 156 | + mock_patch.assert_not_called() | ||
| 157 | + | ||
| 158 | + mock_patch.reset_mock() | ||
| 159 | + # _dvm_generate_fallback_kernel pops fused_id from _fused_metas; re-seed | ||
| 160 | + # before exercising the static-shape branch in the same test. | ||
| 161 | + _fused_metas[0] = meta | ||
| 162 | + with mock.patch( | ||
| 163 | + "torch_npu._inductor.dvm.graph_fusion.is_fx_dynamic", return_value=False | ||
| 164 | + ): | ||
| 165 | + _dvm_generate_fallback_kernel( | ||
| 166 | + self._make_codegen_wrapper(), | ||
| 167 | + self._make_fallback_kernel(), | ||
| 168 | + ) | ||
| 169 | + mock_patch.assert_called_once_with(meta.gm, ["buf0"]) | ||
| 170 | + finally: | ||
| 171 | + _fused_metas.pop(0, None) | ||
| 172 | + | ||
| 173 | + | ||
| 174 | +if __name__ == "__main__": | ||
| 175 | + run_tests() | ||
| @@ -0,0 +1,39 @@ | |||
| 1 | +import torch | ||
| 2 | + | ||
| 3 | +from torch.testing._internal.common_utils import TestCase | ||
| 4 | +from torch.testing._internal.common_utils import run_tests | ||
| 5 | + | ||
| 6 | +from torch_npu._inductor import dvm | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +def fused_add_sum(k: dvm.Kernel): | ||
| 10 | + x = k.load([-1, -1, -1], dvm.float32) | ||
| 11 | + y = k.load([-1, -1, -1], dvm.float32) | ||
| 12 | + scalar = k.scalar(dvm.float32) | ||
| 13 | + a = k.add(x, y) | ||
| 14 | + b = k.add(a, scalar) | ||
| 15 | + c = k.sum(b, [0, 1], True) | ||
| 16 | + k.store(c) | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +class TestDvmKernelOp(TestCase): | ||
| 20 | + def test_dvm_kernel_op(self): | ||
| 21 | + a = torch.normal(0, 0.1, size=(512, 128, 256), dtype=torch.float32).npu() | ||
| 22 | + b = torch.normal(0, 0.1, size=(512, 1, 256), dtype=torch.float32).npu() | ||
| 23 | + scalar = 1.22 | ||
| 24 | + expect = torch.sum((a + b + 1.22), dim=[0, 1], keepdim=True) | ||
| 25 | + result = torch.empty((1, 1, 256), device="npu") | ||
| 26 | + kernel1 = dvm.kernel(ktype="vector", dyn_shape=True)(fused_add_sum) | ||
| 27 | + kernel1.run(a, b, scalar, result) | ||
| 28 | + kernel1.run(a, b, scalar, result) | ||
| 29 | + kernel1.run(a, b, scalar, result) | ||
| 30 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 31 | + kernel2 = dvm.kernel(ktype="split", dyn_shape=True)(fused_add_sum) | ||
| 32 | + result = kernel2(a, b, scalar) | ||
| 33 | + result = kernel2(a, b, scalar) | ||
| 34 | + result = kernel2(a, b, scalar) | ||
| 35 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +if __name__ == "__main__": | ||
| 39 | + run_tests() | ||
| @@ -0,0 +1,321 @@ | |||
| 1 | +# Owner(s): ["module: tests"] | ||
| 2 | +import os | ||
| 3 | + | ||
| 4 | +import torch | ||
| 5 | +from torch._inductor.utils import run_and_get_code | ||
| 6 | +from torch.testing._internal.common_utils import ( | ||
| 7 | + instantiate_parametrized_tests, | ||
| 8 | + parametrize, | ||
| 9 | + run_tests, | ||
| 10 | + TestCase, | ||
| 11 | +) | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +class TestModule(torch.nn.Module): | ||
| 15 | + def __init__(self, *args, **kwargs): | ||
| 16 | + super().__init__(*args, **kwargs) | ||
| 17 | + | ||
| 18 | + def forward(self, a, b, c): | ||
| 19 | + b = torch.transpose(b, 0, 1) | ||
| 20 | + add = a + b | ||
| 21 | + sub = c - a | ||
| 22 | + mul = add * sub | ||
| 23 | + mul = mul + 3 | ||
| 24 | + return mul, torch.sum(mul, dim=[0, 2], keepdim=True) | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +class ReduceCaseModel(torch.nn.Module): | ||
| 28 | + def __init__(self): | ||
| 29 | + super().__init__() | ||
| 30 | + | ||
| 31 | + def forward(self, arg0_1, arg1_1, arg2_1): | ||
| 32 | + sum_1 = torch.ops.aten.sum.dim_IntList(arg0_1, [0, 2, 3], True) | ||
| 33 | + div = torch.ops.aten.div.Scalar(sum_1, 9800.0) | ||
| 34 | + view = torch.ops.aten.reshape.default(div, [64]) | ||
| 35 | + mul = torch.ops.aten.mul.Scalar(view, 0.1) | ||
| 36 | + mul_1 = torch.ops.aten.mul.Scalar(arg1_1, 0.9) | ||
| 37 | + add = torch.ops.aten.add.Tensor(mul, mul_1) | ||
| 38 | + expand = torch.ops.aten.expand.default(div, [8, 64, 35, 35]) | ||
| 39 | + sub = torch.ops.aten.sub.Tensor(arg0_1, expand) | ||
| 40 | + pow_1 = torch.ops.aten.pow.Tensor_Scalar(sub, 2) | ||
| 41 | + sum_2 = torch.ops.aten.sum.dim_IntList(pow_1, [0, 2, 3], True) | ||
| 42 | + div_1 = torch.ops.aten.div.Scalar(sum_2, 9800.0) | ||
| 43 | + add_1 = torch.ops.aten.add.Scalar(div_1, 0.001) | ||
| 44 | + rsqrt = torch.ops.aten.rsqrt.default(add_1) | ||
| 45 | + view_1 = torch.ops.aten.reshape.default(div_1, [64]) | ||
| 46 | + mul_2 = torch.ops.aten.mul.Scalar(view_1, 1.0001020512297174) | ||
| 47 | + mul_3 = torch.ops.aten.mul.Scalar(mul_2, 0.1) | ||
| 48 | + mul_4 = torch.ops.aten.mul.Scalar(arg2_1, 0.9) | ||
| 49 | + add_2 = torch.ops.aten.add.Tensor(mul_3, mul_4) | ||
| 50 | + return (div, add, rsqrt, add_2) | ||
| 51 | + | ||
| 52 | + | ||
| 53 | +class DeterministicReduceModel(torch.nn.Module): | ||
| 54 | + def __init__(self): | ||
| 55 | + super().__init__() | ||
| 56 | + | ||
| 57 | + def forward(self, arg0): | ||
| 58 | + return torch.ops.aten.sum.default(arg0) | ||
| 59 | + | ||
| 60 | + | ||
| 61 | +class BitwiseBoolModel(torch.nn.Module): | ||
| 62 | + def forward(self, arg0, arg1): | ||
| 63 | + bitwise_and = torch.ops.aten.bitwise_and.Tensor(arg0, arg1) | ||
| 64 | + bitwise_not = torch.ops.aten.bitwise_not.default(arg0) | ||
| 65 | + return torch.ops.aten.bitwise_or.Tensor(bitwise_and, bitwise_not) | ||
| 66 | + | ||
| 67 | + | ||
| 68 | +class BitwiseIntModel(torch.nn.Module): | ||
| 69 | + def forward(self, arg0): | ||
| 70 | + return torch.ops.aten.bitwise_not.default(arg0) | ||
| 71 | + | ||
| 72 | + | ||
| 73 | +class MmTransposeBackwardModel(torch.nn.Module): | ||
| 74 | + def forward(self, a, b): | ||
| 75 | + loss = torch.mm(a, b.t()).sum() | ||
| 76 | + grad_a, grad_b = torch.autograd.grad(loss, (a, b)) | ||
| 77 | + return loss, grad_a, grad_b | ||
| 78 | + | ||
| 79 | + | ||
| 80 | +class CopyInplaceModel(torch.nn.Module): | ||
| 81 | + def forward(self, dst, src): | ||
| 82 | + add = torch.ops.aten.add.Tensor(src, 1.0) | ||
| 83 | + torch.ops.aten.copy_.default(dst, add) | ||
| 84 | + return () | ||
| 85 | + | ||
| 86 | + | ||
| 87 | +class Int64AddModel(torch.nn.Module): | ||
| 88 | + def forward(self, x, y): | ||
| 89 | + return x + y | ||
| 90 | + | ||
| 91 | + | ||
| 92 | +class Int64CompareModel(torch.nn.Module): | ||
| 93 | + def forward(self, x, y): | ||
| 94 | + return x > y | ||
| 95 | + | ||
| 96 | + | ||
| 97 | +class Int64PointwiseFusionModel(torch.nn.Module): | ||
| 98 | + def forward(self, x, y, ids): | ||
| 99 | + sum_xy = x + y | ||
| 100 | + mask = sum_xy > y | ||
| 101 | + pointwise = torch.where(mask, sum_xy, x) | ||
| 102 | + dense_mask = torch.unsqueeze( | ||
| 103 | + torch.where( | ||
| 104 | + ids >= 0, | ||
| 105 | + torch.ones_like(ids, dtype=torch.float32), | ||
| 106 | + torch.zeros_like(ids), | ||
| 107 | + ), | ||
| 108 | + dim=-1, | ||
| 109 | + ) | ||
| 110 | + ids = torch.where(ids == -1, torch.zeros_like(ids), ids) | ||
| 111 | + return pointwise, dense_mask, ids | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +class TestDvmByMlir(TestCase): | ||
| 115 | + def _run_and_get_code_with_dvm(self, model, *args): | ||
| 116 | + original_backend = os.environ.get("TORCHINDUCTOR_NPU_BACKEND") | ||
| 117 | + os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm" | ||
| 118 | + try: | ||
| 119 | + compiled_model = torch.compile(model, backend="inductor", dynamic=False) | ||
| 120 | + return run_and_get_code(compiled_model, *args) | ||
| 121 | + finally: | ||
| 122 | + if original_backend is None: | ||
| 123 | + os.environ.pop("TORCHINDUCTOR_NPU_BACKEND", None) | ||
| 124 | + else: | ||
| 125 | + os.environ["TORCHINDUCTOR_NPU_BACKEND"] = original_backend | ||
| 126 | + | ||
| 127 | + def test_int64_add_fuses_into_dvm(self): | ||
| 128 | + arg0 = torch.randint(-8, 8, (32, 32), dtype=torch.int64, device="npu") | ||
| 129 | + arg1 = torch.randint(-8, 8, (32, 32), dtype=torch.int64, device="npu") | ||
| 130 | + model = Int64AddModel() | ||
| 131 | + | ||
| 132 | + with torch.no_grad(): | ||
| 133 | + expect = model(arg0, arg1) | ||
| 134 | + result, codes = self._run_and_get_code_with_dvm(model, arg0, arg1) | ||
| 135 | + | ||
| 136 | + code = "\n".join(codes) | ||
| 137 | + self.assertEqual(expect, result) | ||
| 138 | + self.assertIn("k.add", code) | ||
| 139 | + | ||
| 140 | + def test_int64_compare_fuses_into_dvm(self): | ||
| 141 | + arg0 = torch.randint(-8, 8, (32, 32), dtype=torch.int64, device="npu") | ||
| 142 | + arg1 = torch.randint(-8, 8, (32, 32), dtype=torch.int64, device="npu") | ||
| 143 | + model = Int64CompareModel() | ||
| 144 | + | ||
| 145 | + with torch.no_grad(): | ||
| 146 | + expect = model(arg0, arg1) | ||
| 147 | + result, codes = self._run_and_get_code_with_dvm(model, arg0, arg1) | ||
| 148 | + | ||
| 149 | + code = "\n".join(codes) | ||
| 150 | + self.assertEqual(expect, result) | ||
| 151 | + self.assertIn("k.greater", code) | ||
| 152 | + | ||
| 153 | + def test_int64_pointwise_chain_fuses_into_dvm(self): | ||
| 154 | + arg0 = torch.randint(-8, 8, (32, 32), dtype=torch.int64, device="npu") | ||
| 155 | + arg1 = torch.randint(-8, 8, (32, 32), dtype=torch.int64, device="npu") | ||
| 156 | + ids = torch.randint(-2, 4, (32, 32), dtype=torch.int64, device="npu") | ||
| 157 | + model = Int64PointwiseFusionModel() | ||
| 158 | + | ||
| 159 | + with torch.no_grad(): | ||
| 160 | + expect = model(arg0, arg1, ids) | ||
| 161 | + result, codes = self._run_and_get_code_with_dvm(model, arg0, arg1, ids) | ||
| 162 | + | ||
| 163 | + code = "\n".join(codes) | ||
| 164 | + self.assertEqual(expect, result) | ||
| 165 | + self.assertIn("k.add", code) | ||
| 166 | + self.assertIn("k.greater", code) | ||
| 167 | + self.assertIn("k.greater_equal", code) | ||
| 168 | + self.assertIn("k.equal", code) | ||
| 169 | + self.assertEqual(code.count("k.select("), 3) | ||
| 170 | + | ||
| 171 | + | ||
| 172 | + | ||
| 173 | + | ||
| 174 | + def test_basic_partitioning(self, dtype, is_dynamic): | ||
| 175 | + os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm" | ||
| 176 | + a = torch.normal(0, 0.01, size=(512, 1), dtype=dtype).npu() | ||
| 177 | + b = torch.normal(0, 0.01, size=(512, 4, 256), dtype=dtype).npu() | ||
| 178 | + c = torch.normal(0, 0.01, size=(1, 256), dtype=dtype).npu() | ||
| 179 | + model = TestModule() | ||
| 180 | + dvm_compiled_model = torch.compile( | ||
| 181 | + model, backend="inductor", dynamic=is_dynamic | ||
| 182 | + ) | ||
| 183 | + with torch.no_grad(): | ||
| 184 | + expect = model(a, b, c) | ||
| 185 | + result = dvm_compiled_model(a, b, c) | ||
| 186 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 187 | + del os.environ["TORCHINDUCTOR_NPU_BACKEND"] | ||
| 188 | + | ||
| 189 | + | ||
| 190 | + | ||
| 191 | + def test_basic_partitioning_npugraph(self, dtype, is_dynamic): | ||
| 192 | + os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm" | ||
| 193 | + a = torch.normal(0, 0.01, size=(512, 1), dtype=dtype).npu() | ||
| 194 | + b = torch.normal(0, 0.01, size=(512, 4, 256), dtype=dtype).npu() | ||
| 195 | + c = torch.normal(0, 0.01, size=(1, 256), dtype=dtype).npu() | ||
| 196 | + model = TestModule() | ||
| 197 | + dvm_compiled_model = torch.compile( | ||
| 198 | + model, | ||
| 199 | + backend="inductor", | ||
| 200 | + dynamic=is_dynamic, | ||
| 201 | + options={"triton.cudagraphs": True}, | ||
| 202 | + ) | ||
| 203 | + with torch.no_grad(): | ||
| 204 | + expect = model(a, b, c) | ||
| 205 | + result = dvm_compiled_model(a, b, c) | ||
| 206 | + result = dvm_compiled_model(a, b, c) | ||
| 207 | + result = dvm_compiled_model(a, b, c) | ||
| 208 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 209 | + del os.environ["TORCHINDUCTOR_NPU_BACKEND"] | ||
| 210 | + | ||
| 211 | + | ||
| 212 | + | ||
| 213 | + def test_reduce_case(self, dtype, is_dynamic): | ||
| 214 | + os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm" | ||
| 215 | + arg0 = torch.empty_strided( | ||
| 216 | + torch.Size((8, 64, 35, 35)), | ||
| 217 | + (78400, 1225, 35, 1), | ||
| 218 | + dtype=dtype, | ||
| 219 | + device="npu", | ||
| 220 | + ).uniform_(0, 1) | ||
| 221 | + arg1 = torch.empty_strided( | ||
| 222 | + torch.Size((64,)), (1,), dtype=dtype, device="npu" | ||
| 223 | + ).uniform_(0, 1) | ||
| 224 | + arg2 = torch.empty_strided( | ||
| 225 | + torch.Size((64,)), (1,), dtype=dtype, device="npu" | ||
| 226 | + ).uniform_(0, 1) | ||
| 227 | + model = ReduceCaseModel() | ||
| 228 | + dvm_compiled_model = torch.compile( | ||
| 229 | + model, backend="inductor", dynamic=is_dynamic | ||
| 230 | + ) | ||
| 231 | + with torch.no_grad(): | ||
| 232 | + expect = model(arg0, arg1, arg2) | ||
| 233 | + result = dvm_compiled_model(arg0, arg1, arg2) | ||
| 234 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 235 | + del os.environ["TORCHINDUCTOR_NPU_BACKEND"] | ||
| 236 | + | ||
| 237 | + def test_deterministic_reduce_case(self): | ||
| 238 | + os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm" | ||
| 239 | + deterministic_state = torch.are_deterministic_algorithms_enabled() | ||
| 240 | + deterministic_warn_only = torch.is_deterministic_algorithms_warn_only_enabled() | ||
| 241 | + arg0 = torch.normal( | ||
| 242 | + 0, 0.1, size=(16, 128, 64, 64), dtype=torch.float32, device="npu" | ||
| 243 | + ) | ||
| 244 | + model = DeterministicReduceModel() | ||
| 245 | + try: | ||
| 246 | + torch.use_deterministic_algorithms(True) | ||
| 247 | + dvm_compiled_model = torch.compile( | ||
| 248 | + model, backend="inductor", dynamic=False | ||
| 249 | + ) | ||
| 250 | + with torch.no_grad(): | ||
| 251 | + first_result = dvm_compiled_model(arg0) | ||
| 252 | + second_result = dvm_compiled_model(arg0) | ||
| 253 | + self.assertEqual(first_result, second_result, atol=0, rtol=0) | ||
| 254 | + finally: | ||
| 255 | + torch.use_deterministic_algorithms( | ||
| 256 | + deterministic_state, warn_only=deterministic_warn_only | ||
| 257 | + ) | ||
| 258 | + del os.environ["TORCHINDUCTOR_NPU_BACKEND"] | ||
| 259 | + | ||
| 260 | + def test_bitwise_bool_ops_codegen(self): | ||
| 261 | + arg0 = torch.randint(0, 2, (32, 32), dtype=torch.bool, device="npu") | ||
| 262 | + arg1 = torch.randint(0, 2, (32, 32), dtype=torch.bool, device="npu") | ||
| 263 | + model = BitwiseBoolModel() | ||
| 264 | + | ||
| 265 | + with torch.no_grad(): | ||
| 266 | + expect = model(arg0, arg1) | ||
| 267 | + result, codes = self._run_and_get_code_with_dvm(model, arg0, arg1) | ||
| 268 | + | ||
| 269 | + code = "\n".join(codes) | ||
| 270 | + self.assertEqual(expect, result) | ||
| 271 | + self.assertIn("k.logical_and", code) | ||
| 272 | + self.assertIn("k.logical_or", code) | ||
| 273 | + self.assertIn("k.logical_not", code) | ||
| 274 | + | ||
| 275 | + def test_bitwise_int_rule_fallback(self): | ||
| 276 | + arg0 = torch.randint(-8, 8, (32, 32), dtype=torch.int32, device="npu") | ||
| 277 | + model = BitwiseIntModel() | ||
| 278 | + | ||
| 279 | + with torch.no_grad(): | ||
| 280 | + expect = model(arg0) | ||
| 281 | + result, codes = self._run_and_get_code_with_dvm(model, arg0) | ||
| 282 | + | ||
| 283 | + code = "\n".join(codes) | ||
| 284 | + self.assertEqual(expect, result) | ||
| 285 | + self.assertNotIn("k.logical_not", code) | ||
| 286 | + | ||
| 287 | + def test_mm_t_backward_no_dvm_fused_matmul_backward(self): | ||
| 288 | + a = torch.randn((4, 8), dtype=torch.float32, device="npu") | ||
| 289 | + b = torch.randn((3, 8), dtype=torch.float32, device="npu") | ||
| 290 | + a_eager = a.detach().clone().requires_grad_(True) | ||
| 291 | + b_eager = b.detach().clone().requires_grad_(True) | ||
| 292 | + a_compiled = a.detach().clone().requires_grad_(True) | ||
| 293 | + b_compiled = b.detach().clone().requires_grad_(True) | ||
| 294 | + model = MmTransposeBackwardModel() | ||
| 295 | + | ||
| 296 | + expect = model(a_eager, b_eager) | ||
| 297 | + result, codes = self._run_and_get_code_with_dvm( | ||
| 298 | + model, a_compiled, b_compiled | ||
| 299 | + ) | ||
| 300 | + | ||
| 301 | + code = "\n".join(codes) | ||
| 302 | + self.assertEqual(expect, result, atol=1e-3, rtol=1e-3) | ||
| 303 | + self.assertNotIn("dvm_fused_matmul_backward", code) | ||
| 304 | + | ||
| 305 | + def test_copy_inplace_codegen(self): | ||
| 306 | + src = torch.randn((128,), dtype=torch.float32, device="npu") | ||
| 307 | + dst = torch.zeros((128,), dtype=torch.float32, device="npu") | ||
| 308 | + expect_dst = dst.clone() | ||
| 309 | + actual_dst = dst.clone() | ||
| 310 | + model = CopyInplaceModel() | ||
| 311 | + | ||
| 312 | + with torch.no_grad(): | ||
| 313 | + model(expect_dst, src) | ||
| 314 | + self._run_and_get_code_with_dvm(model, actual_dst, src) | ||
| 315 | + | ||
| 316 | + self.assertEqual(expect_dst, actual_dst) | ||
| 317 | + | ||
| 318 | + | ||
| 319 | +instantiate_parametrized_tests(TestDvmByMlir) | ||
| 320 | +if __name__ == "__main__": | ||
| 321 | + run_tests() | ||
| @@ -666,6 +666,14 @@ class TestPublicBindings(TestCase): | |||
| 666 | "torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.ir", | 666 | "torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.ir", |
| 667 | "torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.lowering", | 667 | "torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.lowering", |
| 668 | "torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.scheduler", | 668 | "torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.scheduler", |
| 669 | + "torch_npu._inductor.dvm", | ||
| 670 | + "torch_npu._inductor.dvm.decomp", | ||
| 671 | + "torch_npu._inductor.dvm.fx_pass", | ||
| 672 | + "torch_npu._inductor.dvm.fx_test", | ||
| 673 | + "torch_npu._inductor.dvm.graph_build", | ||
| 674 | + "torch_npu._inductor.dvm.graph_fusion", | ||
| 675 | + "torch_npu._inductor.dvm.mlir_fusion", | ||
| 676 | + "torch_npu._inductor.dvm.op_emitter", | ||
| 669 | } | 677 | } |
| 670 | 678 | ||
| 671 | # No new entries should be added to this list. | 679 | # No new entries should be added to this list. |
| @@ -1,254 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * @file prof_api.h | ||
| 3 | - * | ||
| 4 | - * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved. | ||
| 5 | - * | ||
| 6 | - * This program is distributed in the hope that it will be useful, | ||
| 7 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 8 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | - * | ||
| 10 | - */ | ||
| 11 | - | ||
| 12 | - | ||
| 13 | - | ||
| 14 | - | ||
| 15 | - | ||
| 16 | - | ||
| 17 | - | ||
| 18 | - | ||
| 19 | - | ||
| 20 | - | ||
| 21 | -extern "C" { | ||
| 22 | - | ||
| 23 | - | ||
| 24 | - | ||
| 25 | - | ||
| 26 | - | ||
| 27 | - | ||
| 28 | - | ||
| 29 | - | ||
| 30 | -/* | ||
| 31 | - * @ingroup libprofapi | ||
| 32 | - * @name profRegReporterCallback | ||
| 33 | - * @brief register report callback interface for atlas | ||
| 34 | - * @param [in] reporter: reporter callback handle | ||
| 35 | - * @return 0:SUCCESS, !0:FAILED | ||
| 36 | - */ | ||
| 37 | -MSVP_PROF_API int32_t profRegReporterCallback(MsprofReportHandle reporter); | ||
| 38 | - | ||
| 39 | -/* | ||
| 40 | - * @ingroup libprofapi | ||
| 41 | - * @name profRegCtrlCallback | ||
| 42 | - * @brief register control callback, interface for atlas | ||
| 43 | - * @param [in] handle: control callback handle | ||
| 44 | - * @return 0:SUCCESS, !0:FAILED | ||
| 45 | - */ | ||
| 46 | -MSVP_PROF_API int32_t profRegCtrlCallback(MsprofCtrlHandle handle); | ||
| 47 | - | ||
| 48 | -/* | ||
| 49 | - * @ingroup libprofapi | ||
| 50 | - * @name profRegDeviceStateCallback | ||
| 51 | - * @brief register device state notify callback, interface for atlas | ||
| 52 | - * @param [in] handle: handle of ProfNotifySetDevice | ||
| 53 | - * @return 0:SUCCESS, !0:FAILED | ||
| 54 | - */ | ||
| 55 | -MSVP_PROF_API int32_t profRegDeviceStateCallback(MsprofSetDeviceHandle handle); | ||
| 56 | - | ||
| 57 | -/* | ||
| 58 | - * @ingroup libprofapi | ||
| 59 | - * @name profGetDeviceIdByGeModelIdx | ||
| 60 | - * @brief get device id by model id, interface for atlas | ||
| 61 | - * @param [in] modelIdx: ge model id | ||
| 62 | - * @param [out] deviceId: device id | ||
| 63 | - * @return 0:SUCCESS, !0:FAILED | ||
| 64 | - */ | ||
| 65 | -MSVP_PROF_API int32_t profGetDeviceIdByGeModelIdx(const uint32_t modelIdx, uint32_t *deviceId); | ||
| 66 | - | ||
| 67 | -/* | ||
| 68 | - * @ingroup libprofapi | ||
| 69 | - * @name profSetProfCommand | ||
| 70 | - * @brief register set profiling command, interface for atlas | ||
| 71 | - * @param [in] command: 0 isn't aging, !0 is aging | ||
| 72 | - * @param [in] len: api of timestamp data | ||
| 73 | - * @return 0:SUCCESS, !0:FAILED | ||
| 74 | - */ | ||
| 75 | -MSVP_PROF_API int32_t profSetProfCommand(VOID_PTR command, uint32_t len); | ||
| 76 | - | ||
| 77 | -/* | ||
| 78 | - * @ingroup libprofapi | ||
| 79 | - * @name profSetStepInfo | ||
| 80 | - * @brief set step info for torch, interface for atlas | ||
| 81 | - * @param [in] indexId: id of iteration index | ||
| 82 | - * @param [in] tagId: id of tag | ||
| 83 | - * @param [in] stream: api of timestamp data | ||
| 84 | - * @return 0:SUCCESS, !0:FAILED | ||
| 85 | - */ | ||
| 86 | -MSVP_PROF_API int32_t profSetStepInfo(const uint64_t indexId, const uint16_t tagId, void* const stream); | ||
| 87 | - | ||
| 88 | -/* | ||
| 89 | - * @ingroup libprofapi | ||
| 90 | - * @name MsprofRegisterProfileCallback | ||
| 91 | - * @brief register profile callback by callback type, interface for atlas | ||
| 92 | - * @param [in] callbackType: type of callback(reporter/ctrl/device state/command) | ||
| 93 | - * @param [in] callback: callback of profile | ||
| 94 | - * @param [in] len: callback length | ||
| 95 | - * @return 0:SUCCESS, !0:FAILED | ||
| 96 | - */ | ||
| 97 | -MSVP_PROF_API int32_t MsprofRegisterProfileCallback(int32_t callbackType, VOID_PTR callback, uint32_t len); | ||
| 98 | - | ||
| 99 | -/** | ||
| 100 | - * @ingroup libprofapi | ||
| 101 | - * @name MsprofInit | ||
| 102 | - * @brief Profiling module init | ||
| 103 | - * @param [in] dataType: profiling type: ACL Env/ACL Json/GE Option | ||
| 104 | - * @param [in] data: profiling switch data | ||
| 105 | - * @param [in] dataLen: Length of data | ||
| 106 | - * @return 0:SUCCESS, >0:FAILED | ||
| 107 | - */ | ||
| 108 | -MSVP_PROF_API int32_t MsprofInit(uint32_t dataType, VOID_PTR data, uint32_t dataLen); | ||
| 109 | - | ||
| 110 | -/** | ||
| 111 | - * @ingroup libprofapi | ||
| 112 | - * @name MsprofSetConfig | ||
| 113 | - * @brief Set profiling config | ||
| 114 | - * @return 0:SUCCESS, !0:FAILED | ||
| 115 | - */ | ||
| 116 | -MSVP_PROF_API int32_t MsprofSetConfig(uint32_t configType, const char *config, size_t configLength); | ||
| 117 | - | ||
| 118 | -/** | ||
| 119 | - * @ingroup libprofapi | ||
| 120 | - * @name MsprofRegisterCallback | ||
| 121 | - * @brief register profiling switch callback for module | ||
| 122 | - * @param [in] agingFlag: 0 isn't aging, !0 is aging | ||
| 123 | - * @param [in] api: api of timestamp data | ||
| 124 | - * @return 0:SUCCESS, !0:FAILED | ||
| 125 | - */ | ||
| 126 | -MSVP_PROF_API int32_t MsprofRegisterCallback(uint32_t moduleId, ProfCommandHandle handle); | ||
| 127 | - | ||
| 128 | -/** | ||
| 129 | - * @ingroup libprofapi | ||
| 130 | - * @name MsprofReportData | ||
| 131 | - * @brief report profiling data of module | ||
| 132 | - * @param [in] moduleId: module id | ||
| 133 | - * @param [in] type: report type(init/uninit/max length/hash) | ||
| 134 | - * @param [in] data: profiling data | ||
| 135 | - * @param [in] len: length of profiling data | ||
| 136 | - * @return 0:SUCCESS, !0:FAILED | ||
| 137 | - */ | ||
| 138 | -MSVP_PROF_API int32_t MsprofReportData(uint32_t moduleId, uint32_t type, VOID_PTR data, uint32_t len); | ||
| 139 | - | ||
| 140 | -/* | ||
| 141 | - * @ingroup libprofapi | ||
| 142 | - * @name MsprofReportApi | ||
| 143 | - * @brief report api timestamp | ||
| 144 | - * @param [in] agingFlag: 0 isn't aging, !0 is aging | ||
| 145 | - * @param [in] api: api of timestamp data | ||
| 146 | - * @return 0:SUCCESS, !0:FAILED | ||
| 147 | - */ | ||
| 148 | -MSVP_PROF_API int32_t MsprofReportApi(uint32_t agingFlag, const struct MsprofApi *api); | ||
| 149 | - | ||
| 150 | -/* | ||
| 151 | - * @ingroup libprofapi | ||
| 152 | - * @name MsprofReportEvent | ||
| 153 | - * @brief report event timestamp | ||
| 154 | - * @param [in] agingFlag: 0 isn't aging, !0 is aging | ||
| 155 | - * @param [in] event: event of timestamp data | ||
| 156 | - * @return 0:SUCCESS, !0:FAILED | ||
| 157 | - */ | ||
| 158 | -MSVP_PROF_API int32_t MsprofReportEvent(uint32_t agingFlag, const struct MsprofEvent *event); | ||
| 159 | - | ||
| 160 | -/* | ||
| 161 | - * @ingroup libprofapi | ||
| 162 | - * @name MsprofReportCompactInfo | ||
| 163 | - * @brief report profiling compact infomation | ||
| 164 | - * @param [in] agingFlag: 0 isn't aging, !0 is aging | ||
| 165 | - * @param [in] data: profiling data of compact infomation | ||
| 166 | - * @param [in] length: length of profiling data | ||
| 167 | - * @return 0:SUCCESS, !0:FAILED | ||
| 168 | - */ | ||
| 169 | -MSVP_PROF_API int32_t MsprofReportCompactInfo(uint32_t agingFlag, const VOID_PTR data, uint32_t length); | ||
| 170 | - | ||
| 171 | -/* | ||
| 172 | - * @ingroup libprofapi | ||
| 173 | - * @name MsprofReportAdditionalInfo | ||
| 174 | - * @brief report profiling additional infomation | ||
| 175 | - * @param [in] agingFlag: 0 isn't aging, !0 is aging | ||
| 176 | - * @param [in] data: profiling data of additional infomation | ||
| 177 | - * @param [in] length: length of profiling data | ||
| 178 | - * @return 0:SUCCESS, !0:FAILED | ||
| 179 | - */ | ||
| 180 | -MSVP_PROF_API int32_t MsprofReportAdditionalInfo(uint32_t agingFlag, const VOID_PTR data, uint32_t length); | ||
| 181 | - | ||
| 182 | -/* | ||
| 183 | - * @ingroup libprofapi | ||
| 184 | - * @name MsprofRegTypeInfo | ||
| 185 | - * @brief reg mapping info of type id and type name | ||
| 186 | - * @param [in] level: level is the report struct's level | ||
| 187 | - * @param [in] typeId: type id is the report struct's type | ||
| 188 | - * @param [in] typeName: label of type id for presenting user | ||
| 189 | - * @return 0:SUCCESS, !0:FAILED | ||
| 190 | - */ | ||
| 191 | -MSVP_PROF_API int32_t MsprofRegTypeInfo(uint16_t level, uint32_t typeId, const char *typeName); | ||
| 192 | - | ||
| 193 | -/* | ||
| 194 | - * @ingroup libprofapi | ||
| 195 | - * @name MsprofGetHashId | ||
| 196 | - * @brief return hash id of hash info | ||
| 197 | - * @param [in] hashInfo: infomation to be hashed | ||
| 198 | - * @param [in] length: the length of infomation to be hashed | ||
| 199 | - * @return hash id | ||
| 200 | - */ | ||
| 201 | -MSVP_PROF_API uint64_t MsprofGetHashId(const char *hashInfo, size_t length); | ||
| 202 | - | ||
| 203 | -/** | ||
| 204 | - * @ingroup libprofapi | ||
| 205 | - * @name MsprofSetDeviceIdByGeModelIdx | ||
| 206 | - * @brief insert device id by model id | ||
| 207 | - * @param [in] geModelIdx: ge model id | ||
| 208 | - * @param [in] deviceId: device id | ||
| 209 | - * @return 0:SUCCESS, !0:FAILED | ||
| 210 | - */ | ||
| 211 | -MSVP_PROF_API int32_t MsprofSetDeviceIdByGeModelIdx(const uint32_t geModelIdx, const uint32_t deviceId); | ||
| 212 | - | ||
| 213 | -/** | ||
| 214 | - * @ingroup libprofapi | ||
| 215 | - * @name MsprofUnsetDeviceIdByGeModelIdx | ||
| 216 | - * @brief delete device id by model id | ||
| 217 | - * @param [in] geModelIdx: ge model id | ||
| 218 | - * @param [in] deviceId: device id | ||
| 219 | - * @return 0:SUCCESS, !0:FAILED | ||
| 220 | - */ | ||
| 221 | -MSVP_PROF_API int32_t MsprofUnsetDeviceIdByGeModelIdx(const uint32_t geModelIdx, const uint32_t deviceId); | ||
| 222 | - | ||
| 223 | -/** | ||
| 224 | - * @ingroup libprofapi | ||
| 225 | - * @name register report interface for atlas | ||
| 226 | - * @brief report api timestamp | ||
| 227 | - * @param [in] chipId: multi die's chip | ||
| 228 | - * @param [in] deviceId: device id | ||
| 229 | - * @param [in] isOpen: device is open | ||
| 230 | - * @return 0:SUCCESS, !0:FAILED | ||
| 231 | - */ | ||
| 232 | -MSVP_PROF_API int32_t MsprofNotifySetDevice(uint32_t chipId, uint32_t deviceId, bool isOpen); | ||
| 233 | - | ||
| 234 | -/** | ||
| 235 | - * @ingroup libprofapi | ||
| 236 | - * @name MsprofFinalize | ||
| 237 | - * @brief profiling finalize | ||
| 238 | - * @return 0:SUCCESS, !0:FAILED | ||
| 239 | - */ | ||
| 240 | -MSVP_PROF_API int32_t MsprofFinalize(); | ||
| 241 | - | ||
| 242 | -/* | ||
| 243 | - * @ingroup libprofapi | ||
| 244 | - * @name MsprofSysCycleTime | ||
| 245 | - * @brief get systime cycle time of CPU | ||
| 246 | - * @return system cycle time of CPU | ||
| 247 | - */ | ||
| 248 | -MSVP_PROF_API uint64_t MsprofSysCycleTime(); | ||
| 249 | - | ||
| 250 | - | ||
| 251 | -} | ||
| 252 | - | ||
| 253 | - | ||
| 254 | - | ||
| @@ -1,1101 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * @file prof_common.h | ||
| 3 | - * | ||
| 4 | - * Copyright (c) Huawei Technologies Co., Ltd. 2019-2024. All rights reserved. | ||
| 5 | - * | ||
| 6 | - * This program is distributed in the hope that it will be useful, | ||
| 7 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 8 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | - * | ||
| 10 | - */ | ||
| 11 | - | ||
| 12 | - | ||
| 13 | - | ||
| 14 | - | ||
| 15 | - | ||
| 16 | - | ||
| 17 | - | ||
| 18 | -extern "C" { | ||
| 19 | - | ||
| 20 | - | ||
| 21 | - | ||
| 22 | - | ||
| 23 | - | ||
| 24 | - | ||
| 25 | -typedef void* VOID_PTR; | ||
| 26 | -typedef const void* ConstVoidPtr; | ||
| 27 | -typedef int32_t (*MsprofReportHandle)(uint32_t moduleId, uint32_t type, VOID_PTR data, uint32_t len); | ||
| 28 | -typedef int32_t (*MsprofCtrlHandle)(uint32_t type, VOID_PTR data, uint32_t len); | ||
| 29 | -typedef int32_t (*MsprofSetDeviceHandle)(VOID_PTR data, uint32_t len); | ||
| 30 | -typedef int32_t (*MsprofCtrlCallback)(uint32_t type, void *data, uint32_t len); | ||
| 31 | -typedef int32_t (*MsprofReporterCallback)(uint32_t moduleId, uint32_t type, void *data, uint32_t len); | ||
| 32 | - | ||
| 33 | -/** | ||
| 34 | - * @name ProfCommandHandle | ||
| 35 | - * @brief callback to start/stop profiling | ||
| 36 | - * @param type [IN] enum call back type | ||
| 37 | - * @param data [IN] callback data | ||
| 38 | - * @param len [IN] callback data size | ||
| 39 | - * @return enum MsprofErrorCode | ||
| 40 | - */ | ||
| 41 | -typedef int32_t (*ProfCommandHandle)(uint32_t type, VOID_PTR data, uint32_t len); | ||
| 42 | - | ||
| 43 | -/* Msprof report level */ | ||
| 44 | - | ||
| 45 | - | ||
| 46 | - | ||
| 47 | - | ||
| 48 | - | ||
| 49 | - | ||
| 50 | - | ||
| 51 | - | ||
| 52 | - | ||
| 53 | - | ||
| 54 | - | ||
| 55 | -/* Msprof report type of acl(20000) level(acl), offset: 0x000000 */ | ||
| 56 | - | ||
| 57 | - | ||
| 58 | - | ||
| 59 | - | ||
| 60 | - | ||
| 61 | - | ||
| 62 | -/* Msprof report type of acl(20000) level(host api), offset: 0x050000 */ | ||
| 63 | - | ||
| 64 | - | ||
| 65 | - | ||
| 66 | - | ||
| 67 | - | ||
| 68 | - | ||
| 69 | -/* Msprof report type of model(15000) level, offset: 0x000000 */ | ||
| 70 | - | ||
| 71 | - | ||
| 72 | - | ||
| 73 | - | ||
| 74 | - | ||
| 75 | - | ||
| 76 | - | ||
| 77 | - | ||
| 78 | - | ||
| 79 | - | ||
| 80 | -/* Msprof report type of node(10000) level, offset: 0x000000 */ | ||
| 81 | - | ||
| 82 | - | ||
| 83 | - | ||
| 84 | - | ||
| 85 | - | ||
| 86 | - | ||
| 87 | - | ||
| 88 | - | ||
| 89 | - | ||
| 90 | - | ||
| 91 | - | ||
| 92 | - | ||
| 93 | -/* Msprof report type of node(10000) level(ge api), offset: 0x010000 */ | ||
| 94 | - | ||
| 95 | - | ||
| 96 | - | ||
| 97 | -/* Msprof report type of aicpu(6000), offset: 0x000000 */ | ||
| 98 | - | ||
| 99 | - | ||
| 100 | - | ||
| 101 | - | ||
| 102 | - | ||
| 103 | - | ||
| 104 | - | ||
| 105 | - | ||
| 106 | -/* Msprof report type of hccl(5500) level(op api), offset: 0x010000 */ | ||
| 107 | - | ||
| 108 | - | ||
| 109 | - | ||
| 110 | - | ||
| 111 | -/* Msprof report type of hccl(4000U) level(dpu), offset: 0x000000 */ | ||
| 112 | - | ||
| 113 | - | ||
| 114 | -/* use with AdprofCheckFeatureIsOn */ | ||
| 115 | - | ||
| 116 | - | ||
| 117 | - | ||
| 118 | - | ||
| 119 | -/* Msprof report type of profiling(4500) */ | ||
| 120 | - | ||
| 121 | - | ||
| 122 | -enum ProfileCallbackType { | ||
| 123 | - PROFILE_CTRL_CALLBACK = 0, | ||
| 124 | - PROFILE_DEVICE_STATE_CALLBACK, | ||
| 125 | - PROFILE_REPORT_API_CALLBACK, | ||
| 126 | - PROFILE_REPORT_EVENT_CALLBACK, | ||
| 127 | - PROFILE_REPORT_COMPACT_CALLBACK, | ||
| 128 | - PROFILE_REPORT_ADDITIONAL_CALLBACK, | ||
| 129 | - PROFILE_REPORT_REG_TYPE_INFO_CALLBACK, | ||
| 130 | - PROFILE_REPORT_GET_HASH_ID_CALLBACK, | ||
| 131 | - PROFILE_HOST_FREQ_IS_ENABLE_CALLBACK, | ||
| 132 | - PROFILE_REPORT_API_C_CALLBACK, | ||
| 133 | - PROFILE_REPORT_EVENT_C_CALLBACK, | ||
| 134 | - PROFILE_REPORT_REG_TYPE_INFO_C_CALLBACK, | ||
| 135 | - PROFILE_REPORT_GET_HASH_ID_C_CALLBACK, | ||
| 136 | - PROFILE_HOST_FREQ_IS_ENABLE_C_CALLBACK, | ||
| 137 | -}; | ||
| 138 | - | ||
| 139 | -enum MsprofDataTag { | ||
| 140 | - MSPROF_ACL_DATA_TAG = 0, // acl data tag, range: 0~19 | ||
| 141 | - MSPROF_GE_DATA_TAG_MODEL_LOAD = 20, // ge data tag, range: 20~39 | ||
| 142 | - MSPROF_GE_DATA_TAG_FUSION = 21, | ||
| 143 | - MSPROF_GE_DATA_TAG_INFER = 22, | ||
| 144 | - MSPROF_GE_DATA_TAG_TASK = 23, | ||
| 145 | - MSPROF_GE_DATA_TAG_TENSOR = 24, | ||
| 146 | - MSPROF_GE_DATA_TAG_STEP = 25, | ||
| 147 | - MSPROF_GE_DATA_TAG_ID_MAP = 26, | ||
| 148 | - MSPROF_GE_DATA_TAG_HOST_SCH = 27, | ||
| 149 | - MSPROF_RUNTIME_DATA_TAG_API = 40, // runtime data tag, range: 40~59 | ||
| 150 | - MSPROF_RUNTIME_DATA_TAG_TRACK = 41, | ||
| 151 | - MSPROF_AICPU_DATA_TAG = 60, // aicpu data tag, range: 60~79 | ||
| 152 | - MSPROF_AICPU_MODEL_TAG = 61, | ||
| 153 | - MSPROF_HCCL_DATA_TAG = 80, // hccl data tag, range: 80~99 | ||
| 154 | - MSPROF_DP_DATA_TAG = 100, // dp data tag, range: 100~119 | ||
| 155 | - MSPROF_MSPROFTX_DATA_TAG = 120, // hccl data tag, range: 120~139 | ||
| 156 | - MSPROF_DATA_TAG_MAX = 65536, // data tag value type is uint16_t | ||
| 157 | -}; | ||
| 158 | - | ||
| 159 | -enum MsprofMindsporeNodeTag { | ||
| 160 | - GET_NEXT_DEQUEUE_WAIT = 1, | ||
| 161 | -}; | ||
| 162 | - | ||
| 163 | -/** | ||
| 164 | - * @brief struct of mixed data | ||
| 165 | - */ | ||
| 166 | - | ||
| 167 | - | ||
| 168 | -enum MsprofMixDataType { | ||
| 169 | - MSPROF_MIX_DATA_HASH_ID = 0, | ||
| 170 | - MSPROF_MIX_DATA_STRING, | ||
| 171 | -}; | ||
| 172 | -struct MsprofMixData { | ||
| 173 | - uint8_t type; // MsprofMixDataType | ||
| 174 | - uint8_t rsv[MSPROF_MIX_DATA_RESERVE_BYTES]; | ||
| 175 | - union { | ||
| 176 | - uint64_t hashId; | ||
| 177 | - char dataStr[MSPROF_MIX_DATA_STRING_LEN]; | ||
| 178 | - } data; | ||
| 179 | -}; | ||
| 180 | - | ||
| 181 | - | ||
| 182 | - | ||
| 183 | -struct MsprofCommandHandleParams { | ||
| 184 | - uint32_t pathLen; | ||
| 185 | - uint32_t storageLimit; // MB | ||
| 186 | - uint32_t profDataLen; | ||
| 187 | - char path[PATH_LEN_MAX + 1]; | ||
| 188 | - char profData[PARAM_LEN_MAX + 1]; | ||
| 189 | -}; | ||
| 190 | - | ||
| 191 | -/** | ||
| 192 | - * @brief profiling command info | ||
| 193 | - */ | ||
| 194 | - | ||
| 195 | -struct MsprofCommandHandle { | ||
| 196 | - uint64_t profSwitch; | ||
| 197 | - uint64_t profSwitchHi; | ||
| 198 | - uint32_t devNums; | ||
| 199 | - uint32_t devIdList[MSPROF_MAX_DEV_NUM]; | ||
| 200 | - uint32_t modelId; | ||
| 201 | - uint32_t type; | ||
| 202 | - uint32_t cacheFlag; | ||
| 203 | - struct MsprofCommandHandleParams params; | ||
| 204 | -}; | ||
| 205 | - | ||
| 206 | -/** | ||
| 207 | - * @brief struct of data reported by acl | ||
| 208 | - */ | ||
| 209 | - | ||
| 210 | - | ||
| 211 | -enum MsprofAclApiType { | ||
| 212 | - MSPROF_ACL_API_TYPE_OP = 1, | ||
| 213 | - MSPROF_ACL_API_TYPE_MODEL, | ||
| 214 | - MSPROF_ACL_API_TYPE_RUNTIME, | ||
| 215 | - MSPROF_ACL_API_TYPE_OTHERS, | ||
| 216 | -}; | ||
| 217 | -struct MsprofAclProfData { | ||
| 218 | - | ||
| 219 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 220 | - uint16_t dataTag = MSPROF_ACL_DATA_TAG; | ||
| 221 | - | ||
| 222 | - uint16_t magicNumber; | ||
| 223 | - uint16_t dataTag; | ||
| 224 | - | ||
| 225 | - uint32_t apiType; // enum MsprofAclApiType | ||
| 226 | - uint64_t beginTime; | ||
| 227 | - uint64_t endTime; | ||
| 228 | - uint32_t processId; | ||
| 229 | - uint32_t threadId; | ||
| 230 | - char apiName[MSPROF_ACL_API_NAME_LEN]; | ||
| 231 | - uint8_t reserve[MSPROF_ACL_DATA_RESERVE_BYTES]; | ||
| 232 | -}; | ||
| 233 | - | ||
| 234 | -/** | ||
| 235 | - * @brief struct of data reported by GE | ||
| 236 | - */ | ||
| 237 | - | ||
| 238 | -struct MsprofGeProfModelLoadData { | ||
| 239 | - | ||
| 240 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 241 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_MODEL_LOAD; | ||
| 242 | - | ||
| 243 | - uint16_t magicNumber; | ||
| 244 | - uint16_t dataTag; | ||
| 245 | - | ||
| 246 | - uint32_t modelId; | ||
| 247 | - struct MsprofMixData modelName; | ||
| 248 | - uint64_t startTime; | ||
| 249 | - uint64_t endTime; | ||
| 250 | - uint8_t reserve[MSPROF_GE_MODELLOAD_DATA_RESERVE_BYTES]; | ||
| 251 | -}; | ||
| 252 | - | ||
| 253 | - | ||
| 254 | - | ||
| 255 | -struct MsprofGeProfFusionData { | ||
| 256 | - | ||
| 257 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 258 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_FUSION; | ||
| 259 | - | ||
| 260 | - uint16_t magicNumber; | ||
| 261 | - uint16_t dataTag; | ||
| 262 | - | ||
| 263 | - uint32_t modelId; | ||
| 264 | - struct MsprofMixData fusionName; | ||
| 265 | - uint64_t inputMemSize; | ||
| 266 | - uint64_t outputMemSize; | ||
| 267 | - uint64_t weightMemSize; | ||
| 268 | - uint64_t workspaceMemSize; | ||
| 269 | - uint64_t totalMemSize; | ||
| 270 | - uint64_t fusionOpNum; | ||
| 271 | - uint64_t fusionOp[MSPROF_GE_FUSION_OP_NUM]; | ||
| 272 | - uint8_t reserve[MSPROF_GE_FUSION_DATA_RESERVE_BYTES]; | ||
| 273 | -}; | ||
| 274 | - | ||
| 275 | - | ||
| 276 | -struct MsprofGeProfInferData { | ||
| 277 | - | ||
| 278 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 279 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_INFER; | ||
| 280 | - | ||
| 281 | - uint16_t magicNumber; | ||
| 282 | - uint16_t dataTag; | ||
| 283 | - | ||
| 284 | - uint32_t modelId; | ||
| 285 | - struct MsprofMixData modelName; | ||
| 286 | - uint32_t requestId; | ||
| 287 | - uint32_t threadId; | ||
| 288 | - uint64_t inputDataStartTime; | ||
| 289 | - uint64_t inputDataEndTime; | ||
| 290 | - uint64_t inferStartTime; | ||
| 291 | - uint64_t inferEndTime; | ||
| 292 | - uint64_t outputDataStartTime; | ||
| 293 | - uint64_t outputDataEndTime; | ||
| 294 | - uint8_t reserve[MSPROF_GE_INFER_DATA_RESERVE_BYTES]; | ||
| 295 | -}; | ||
| 296 | - | ||
| 297 | - | ||
| 298 | - | ||
| 299 | -enum MsprofGeTaskType { | ||
| 300 | - MSPROF_GE_TASK_TYPE_AI_CORE = 0, | ||
| 301 | - MSPROF_GE_TASK_TYPE_AI_CPU, | ||
| 302 | - MSPROF_GE_TASK_TYPE_AIV, | ||
| 303 | - MSPROF_GE_TASK_TYPE_WRITE_BACK, | ||
| 304 | - MSPROF_GE_TASK_TYPE_MIX_AIC, | ||
| 305 | - MSPROF_GE_TASK_TYPE_MIX_AIV, | ||
| 306 | - MSPROF_GE_TASK_TYPE_FFTS_PLUS, | ||
| 307 | - MSPROF_GE_TASK_TYPE_DSA, | ||
| 308 | - MSPROF_GE_TASK_TYPE_DVPP, | ||
| 309 | - MSPROF_GE_TASK_TYPE_HCCL, | ||
| 310 | - MSPROF_GE_TASK_TYPE_FUSION, | ||
| 311 | - MSPROF_GE_TASK_TYPE_INVALID | ||
| 312 | -}; | ||
| 313 | - | ||
| 314 | -enum MsprofGeShapeType { | ||
| 315 | - MSPROF_GE_SHAPE_TYPE_STATIC = 0, | ||
| 316 | - MSPROF_GE_SHAPE_TYPE_DYNAMIC, | ||
| 317 | -}; | ||
| 318 | -struct MsprofGeOpType { | ||
| 319 | - uint8_t type; // MsprofMixDataType | ||
| 320 | - uint8_t rsv[MSPROF_MIX_DATA_RESERVE_BYTES]; | ||
| 321 | - union { | ||
| 322 | - uint64_t hashId; | ||
| 323 | - char dataStr[MSPROF_GE_OP_TYPE_LEN]; | ||
| 324 | - } data; | ||
| 325 | -}; | ||
| 326 | -struct MsprofGeProfTaskData { | ||
| 327 | - | ||
| 328 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 329 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_TASK; | ||
| 330 | - | ||
| 331 | - uint16_t magicNumber; | ||
| 332 | - uint16_t dataTag; | ||
| 333 | - | ||
| 334 | - uint32_t taskType; // MsprofGeTaskType | ||
| 335 | - struct MsprofMixData opName; | ||
| 336 | - struct MsprofGeOpType opType; | ||
| 337 | - uint64_t curIterNum; | ||
| 338 | - uint64_t timeStamp; | ||
| 339 | - uint32_t shapeType; // MsprofGeShapeType | ||
| 340 | - uint32_t blockDims; | ||
| 341 | - uint32_t modelId; | ||
| 342 | - uint32_t streamId; | ||
| 343 | - uint32_t taskId; | ||
| 344 | - uint32_t threadId; | ||
| 345 | - uint32_t contextId; | ||
| 346 | - uint8_t reserve[MSPROF_GE_TASK_DATA_RESERVE_BYTES]; | ||
| 347 | -}; | ||
| 348 | - | ||
| 349 | - | ||
| 350 | - | ||
| 351 | - | ||
| 352 | -enum MsprofGeTensorType { | ||
| 353 | - MSPROF_GE_TENSOR_TYPE_INPUT = 0, | ||
| 354 | - MSPROF_GE_TENSOR_TYPE_OUTPUT, | ||
| 355 | -}; | ||
| 356 | -struct MsprofGeTensorData { | ||
| 357 | - uint32_t tensorType; // MsprofGeTensorType | ||
| 358 | - uint32_t format; | ||
| 359 | - uint32_t dataType; | ||
| 360 | - uint32_t shape[MSPROF_GE_TENSOR_DATA_SHAPE_LEN]; | ||
| 361 | -}; | ||
| 362 | - | ||
| 363 | -struct MsprofGeProfTensorData { | ||
| 364 | - | ||
| 365 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 366 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_TENSOR; | ||
| 367 | - | ||
| 368 | - uint16_t magicNumber; | ||
| 369 | - uint16_t dataTag; | ||
| 370 | - | ||
| 371 | - uint32_t modelId; | ||
| 372 | - uint64_t curIterNum; | ||
| 373 | - uint32_t streamId; | ||
| 374 | - uint32_t taskId; | ||
| 375 | - uint32_t tensorNum; | ||
| 376 | - struct MsprofGeTensorData tensorData[MSPROF_GE_TENSOR_DATA_NUM]; | ||
| 377 | - uint8_t reserve[MSPROF_GE_TENSOR_DATA_RESERVE_BYTES]; | ||
| 378 | -}; | ||
| 379 | - | ||
| 380 | - | ||
| 381 | -enum MsprofGeStepTag { | ||
| 382 | - MSPROF_GE_STEP_TAG_BEGIN = 0, | ||
| 383 | - MSPROF_GE_STEP_TAG_END, | ||
| 384 | -}; | ||
| 385 | -struct MsprofGeProfStepData { | ||
| 386 | - | ||
| 387 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 388 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_STEP; | ||
| 389 | - | ||
| 390 | - uint16_t magicNumber; | ||
| 391 | - uint16_t dataTag; | ||
| 392 | - | ||
| 393 | - uint32_t modelId; | ||
| 394 | - uint32_t streamId; | ||
| 395 | - uint32_t taskId; | ||
| 396 | - uint64_t timeStamp; | ||
| 397 | - uint64_t curIterNum; | ||
| 398 | - uint32_t threadId; | ||
| 399 | - uint8_t tag; // MsprofGeStepTag | ||
| 400 | - uint8_t reserve[MSPROF_GE_STEP_DATA_RESERVE_BYTES]; | ||
| 401 | -}; | ||
| 402 | - | ||
| 403 | - | ||
| 404 | -struct MsprofGeProfIdMapData { | ||
| 405 | - | ||
| 406 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 407 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_ID_MAP; | ||
| 408 | - | ||
| 409 | - uint16_t magicNumber; | ||
| 410 | - uint16_t dataTag; | ||
| 411 | - | ||
| 412 | - uint32_t graphId; | ||
| 413 | - uint32_t modelId; | ||
| 414 | - uint32_t sessionId; | ||
| 415 | - uint64_t timeStamp; | ||
| 416 | - uint16_t mode; | ||
| 417 | - uint8_t reserve[MSPROF_GE_ID_MAP_DATA_RESERVE_BYTES]; | ||
| 418 | -}; | ||
| 419 | - | ||
| 420 | - | ||
| 421 | -struct MsprofGeProfHostSchData { | ||
| 422 | - | ||
| 423 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 424 | - uint16_t dataTag = MSPROF_GE_DATA_TAG_HOST_SCH; | ||
| 425 | - | ||
| 426 | - uint16_t magicNumber; | ||
| 427 | - uint16_t dataTag; | ||
| 428 | - | ||
| 429 | - uint32_t threadId; // record in start event | ||
| 430 | - uint64_t element; | ||
| 431 | - uint64_t event; | ||
| 432 | - uint64_t startTime; // record in start event | ||
| 433 | - uint64_t endTime; // record in end event | ||
| 434 | - uint8_t reserve[MSPROF_GE_HOST_SCH_DATA_RESERVE_BYTES]; | ||
| 435 | -}; | ||
| 436 | - | ||
| 437 | -/** | ||
| 438 | - * @brief struct of data reported by RunTime | ||
| 439 | - */ | ||
| 440 | - | ||
| 441 | -struct MsprofAicpuProfData { | ||
| 442 | - | ||
| 443 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 444 | - uint16_t dataTag = MSPROF_AICPU_DATA_TAG; | ||
| 445 | - | ||
| 446 | - uint16_t magicNumber; | ||
| 447 | - uint16_t dataTag; | ||
| 448 | - | ||
| 449 | - uint16_t streamId; | ||
| 450 | - uint16_t taskId; | ||
| 451 | - uint64_t runStartTime; | ||
| 452 | - uint64_t runStartTick; | ||
| 453 | - uint64_t computeStartTime; | ||
| 454 | - uint64_t memcpyStartTime; | ||
| 455 | - uint64_t memcpyEndTime; | ||
| 456 | - uint64_t runEndTime; | ||
| 457 | - uint64_t runEndTick; | ||
| 458 | - uint32_t threadId; | ||
| 459 | - uint32_t deviceId; | ||
| 460 | - uint64_t submitTick; | ||
| 461 | - uint64_t scheduleTick; | ||
| 462 | - uint64_t tickBeforeRun; | ||
| 463 | - uint64_t tickAfterRun; | ||
| 464 | - uint32_t kernelType; | ||
| 465 | - uint32_t dispatchTime; | ||
| 466 | - uint32_t totalTime; | ||
| 467 | - uint16_t fftsThreadId; | ||
| 468 | - uint8_t version; | ||
| 469 | - uint8_t reserve[MSPROF_AICPU_DATA_RESERVE_BYTES]; | ||
| 470 | -}; | ||
| 471 | - | ||
| 472 | -struct MsprofAicpuModelProfData { | ||
| 473 | - | ||
| 474 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 475 | - uint16_t dataTag = MSPROF_AICPU_MODEL_TAG; | ||
| 476 | - | ||
| 477 | - uint16_t magicNumber; | ||
| 478 | - uint16_t dataTag; | ||
| 479 | - | ||
| 480 | - uint32_t rsv; // Ensure 8-byte alignment | ||
| 481 | - uint64_t timeStamp; | ||
| 482 | - uint64_t indexId; | ||
| 483 | - uint32_t modelId; | ||
| 484 | - uint16_t tagId; | ||
| 485 | - uint16_t rsv1; | ||
| 486 | - uint64_t eventId; | ||
| 487 | - uint8_t reserve[24]; | ||
| 488 | -}; | ||
| 489 | - | ||
| 490 | -/** | ||
| 491 | - * @brief struct of data reported by DP | ||
| 492 | - */ | ||
| 493 | - | ||
| 494 | - | ||
| 495 | - | ||
| 496 | - | ||
| 497 | - | ||
| 498 | -struct MsprofDpProfData { | ||
| 499 | - | ||
| 500 | - uint16_t magicNumber = MSPROF_DATA_HEAD_MAGIC_NUM; | ||
| 501 | - uint16_t dataTag = MSPROF_DP_DATA_TAG; | ||
| 502 | - | ||
| 503 | - uint16_t magicNumber; | ||
| 504 | - uint16_t dataTag; | ||
| 505 | - | ||
| 506 | - uint32_t rsv; // Ensure 8-byte alignment | ||
| 507 | - uint64_t timeStamp; | ||
| 508 | - char action[MSPROF_DP_DATA_ACTION_LEN]; | ||
| 509 | - char source[MSPROF_DP_DATA_SOURCE_LEN]; | ||
| 510 | - uint64_t index; | ||
| 511 | - uint64_t size; | ||
| 512 | - uint8_t reserve[MSPROF_DP_DATA_RESERVE_BYTES]; | ||
| 513 | -}; | ||
| 514 | - | ||
| 515 | -struct MsprofAicpuNodeAdditionalData { | ||
| 516 | - uint16_t streamId; | ||
| 517 | - uint16_t taskId; | ||
| 518 | - uint64_t runStartTime; | ||
| 519 | - uint64_t runStartTick; | ||
| 520 | - uint64_t computeStartTime; | ||
| 521 | - uint64_t memcpyStartTime; | ||
| 522 | - uint64_t memcpyEndTime; | ||
| 523 | - uint64_t runEndTime; | ||
| 524 | - uint64_t runEndTick; | ||
| 525 | - uint32_t threadId; | ||
| 526 | - uint32_t deviceId; | ||
| 527 | - uint64_t submitTick; | ||
| 528 | - uint64_t scheduleTick; | ||
| 529 | - uint64_t tickBeforeRun; | ||
| 530 | - uint64_t tickAfterRun; | ||
| 531 | - uint32_t kernelType; | ||
| 532 | - uint32_t dispatchTime; | ||
| 533 | - uint32_t totalTime; | ||
| 534 | - uint16_t fftsThreadId; | ||
| 535 | - uint8_t version; | ||
| 536 | - uint8_t reserve[MSPROF_AICPU_DATA_RESERVE_BYTES]; | ||
| 537 | -}; | ||
| 538 | - | ||
| 539 | -struct MsprofAicpuModelAdditionalData { | ||
| 540 | - uint64_t indexId; | ||
| 541 | - uint32_t modelId; | ||
| 542 | - uint16_t tagId; | ||
| 543 | - uint16_t rsv1; | ||
| 544 | - uint64_t eventId; | ||
| 545 | - uint8_t reserve[24]; | ||
| 546 | -}; | ||
| 547 | - | ||
| 548 | -struct MsprofAicpuDpAdditionalData { | ||
| 549 | - char action[MSPROF_DP_DATA_ACTION_LEN]; | ||
| 550 | - char source[MSPROF_DP_DATA_SOURCE_LEN]; | ||
| 551 | - uint64_t index; | ||
| 552 | - uint64_t size; | ||
| 553 | - uint8_t reserve[MSPROF_DP_DATA_RESERVE_BYTES]; | ||
| 554 | -}; | ||
| 555 | - | ||
| 556 | -struct MsprofAicpuMiAdditionalData { | ||
| 557 | - uint32_t nodeTag; // MsprofMindsporeNodeTag:1 | ||
| 558 | - uint32_t reserve; | ||
| 559 | - uint64_t queueSize; | ||
| 560 | - uint64_t runStartTime; | ||
| 561 | - uint64_t runEndTime; | ||
| 562 | -}; | ||
| 563 | - | ||
| 564 | -// AICPU kfc算子执行时间 | ||
| 565 | -struct AicpuKfcProfCommTurn { | ||
| 566 | - uint64_t waitNotifyStartTime; // 开始等待通信参数 | ||
| 567 | - uint64_t kfcAlgExeStartTime; // 开始通信算法执行 | ||
| 568 | - uint64_t sendTaskStartTime; // 开始下发task | ||
| 569 | - uint64_t waitActiveStartTime; // 开始等待激活 | ||
| 570 | - uint64_t acitveStartTime; // 开始激活处理 | ||
| 571 | - uint64_t waitExeEndStartTime; // 开始等待任务执行结束 | ||
| 572 | - uint64_t rtsqExeEndTime; // 任务执行结束时间 | ||
| 573 | - uint64_t dataLen; // 本轮通信数据长度 | ||
| 574 | - uint32_t deviceId; | ||
| 575 | - uint16_t streamId; | ||
| 576 | - uint16_t taskId; | ||
| 577 | - uint8_t version; | ||
| 578 | - uint8_t commTurn; // 总通信轮次 | ||
| 579 | - uint8_t currentTurn; | ||
| 580 | - uint8_t reserve[5]; | ||
| 581 | -}; | ||
| 582 | - | ||
| 583 | -// Aicore算子执行时间 | ||
| 584 | -struct AicpuKfcProfComputeTurn { | ||
| 585 | - uint64_t waitComputeStartTime; // 开始等待计算 | ||
| 586 | - uint64_t computeStartTime; // 开始计算 | ||
| 587 | - uint64_t computeExeEndTime; // 计算执行结束 | ||
| 588 | - uint64_t dataLen; // 本轮计算数据长度 | ||
| 589 | - uint32_t deviceId; | ||
| 590 | - uint16_t streamId; | ||
| 591 | - uint16_t taskId; | ||
| 592 | - uint8_t version; | ||
| 593 | - uint8_t computeTurn; // 总计算轮次 | ||
| 594 | - uint8_t currentTurn; | ||
| 595 | - uint8_t reserve[5]; | ||
| 596 | -}; | ||
| 597 | - | ||
| 598 | -/** | ||
| 599 | - * @brief struct of data reported by HCCL | ||
| 600 | - */ | ||
| 601 | - | ||
| 602 | -struct MsprofHcclProfNotify { | ||
| 603 | - uint32_t taskID; | ||
| 604 | - uint64_t notifyID; | ||
| 605 | - uint32_t stage; | ||
| 606 | - uint32_t remoteRank; | ||
| 607 | - uint32_t transportType; | ||
| 608 | - uint32_t role; // role {0: dst, 1:src} | ||
| 609 | - double durationEstimated; | ||
| 610 | -}; | ||
| 611 | - | ||
| 612 | -struct MsprofHcclProfReduce { | ||
| 613 | - uint32_t taskID; | ||
| 614 | - uint64_t src; | ||
| 615 | - uint64_t dst; | ||
| 616 | - uint64_t size; | ||
| 617 | - uint32_t op; // {0: sum, 1: mul, 2: max, 3: min} | ||
| 618 | - uint32_t dataType; // data type {0: INT8, 1: INT16, 2: INT32, 3: FP16, 4:FP32, 5:INT64, 6:UINT64} | ||
| 619 | - uint32_t linkType; // link type {0: 'OnChip', 1: 'HCCS', 2: 'PCIe', 3: 'RoCE'} | ||
| 620 | - uint32_t remoteRank; | ||
| 621 | - uint32_t transportType; // transport type {0: SDMA, 1: RDMA, 2:LOCAL} | ||
| 622 | - uint32_t role; // role {0: dst, 1:src} | ||
| 623 | - double durationEstimated; | ||
| 624 | -}; | ||
| 625 | - | ||
| 626 | -struct MsprofHcclProfRDMA { | ||
| 627 | - uint32_t taskID; | ||
| 628 | - uint64_t src; | ||
| 629 | - uint64_t dst; | ||
| 630 | - uint64_t size; | ||
| 631 | - uint64_t notifyID; | ||
| 632 | - uint32_t linkType; // link type {0: 'OnChip', 1: 'HCCS', 2: 'PCIe', 3: 'RoCE'} | ||
| 633 | - uint32_t remoteRank; | ||
| 634 | - uint32_t transportType; // transport type {0: RDMA, 1:SDMA, 2:LOCAL} | ||
| 635 | - uint32_t role; // role {0: dst, 1:src} | ||
| 636 | - uint32_t type; // RDMA type {0: RDMASendNotify, 1:RDMASendPayload} | ||
| 637 | - double durationEstimated; | ||
| 638 | -}; | ||
| 639 | - | ||
| 640 | -struct MsprofHcclProfMemcpy { | ||
| 641 | - uint32_t taskID; | ||
| 642 | - uint64_t src; | ||
| 643 | - uint64_t dst; | ||
| 644 | - uint64_t size; | ||
| 645 | - uint64_t notifyID; | ||
| 646 | - uint32_t linkType; // link type {0: 'OnChip', 1: 'HCCS', 2: 'PCIe', 3: 'RoCE'} | ||
| 647 | - uint32_t remoteRank; | ||
| 648 | - uint32_t transportType; // transport type {0: RDMA, 1:SDMA, 2:LOCAL} | ||
| 649 | - uint32_t role; // role {0: dst, 1:src} | ||
| 650 | - double durationEstimated; | ||
| 651 | -}; | ||
| 652 | - | ||
| 653 | -struct MsprofHcclProfStageStep { | ||
| 654 | - uint32_t rank; | ||
| 655 | - uint32_t rankSize; | ||
| 656 | -}; | ||
| 657 | - | ||
| 658 | -struct MsprofHcclProfFlag { | ||
| 659 | - uint64_t cclTag; | ||
| 660 | - uint64_t groupName; | ||
| 661 | - uint32_t localRank; | ||
| 662 | - uint32_t workFlowMode; | ||
| 663 | -}; | ||
| 664 | - | ||
| 665 | - | ||
| 666 | -struct MsprofHcclInfo { | ||
| 667 | - uint64_t itemId; | ||
| 668 | - uint64_t cclTag; | ||
| 669 | - uint64_t groupName; | ||
| 670 | - uint32_t localRank; | ||
| 671 | - uint32_t remoteRank; | ||
| 672 | - uint32_t rankSize; | ||
| 673 | - uint32_t workFlowMode; | ||
| 674 | - uint32_t planeID; | ||
| 675 | - uint32_t ctxId; | ||
| 676 | - uint64_t notifyID; | ||
| 677 | - uint32_t stage; | ||
| 678 | - uint32_t role; // role {0: dst, 1:src} | ||
| 679 | - double durationEstimated; | ||
| 680 | - uint64_t srcAddr; | ||
| 681 | - uint64_t dstAddr; | ||
| 682 | - uint64_t dataSize; // bytes | ||
| 683 | - uint32_t opType; // {0: sum, 1: mul, 2: max, 3: min} | ||
| 684 | - uint32_t dataType; // data type {0: INT8, 1: INT16, 2: INT32, 3: FP16, 4:FP32, 5:INT64, 6:UINT64} | ||
| 685 | - uint32_t linkType; // link type {0: 'OnChip', 1: 'HCCS', 2: 'PCIe', 3: 'RoCE'} | ||
| 686 | - uint32_t transportType; // transport type {0: SDMA, 1: RDMA, 2:LOCAL} | ||
| 687 | - uint32_t rdmaType; // RDMA type {0: RDMASendNotify, 1:RDMASendPayload} | ||
| 688 | - uint32_t reserve2; | ||
| 689 | - | ||
| 690 | - MsprofHcclInfo() : role(MSPROF_HCCL_INVALID_UINT), opType(MSPROF_HCCL_INVALID_UINT), | ||
| 691 | - dataType(MSPROF_HCCL_INVALID_UINT), linkType(MSPROF_HCCL_INVALID_UINT), | ||
| 692 | - transportType(MSPROF_HCCL_INVALID_UINT), rdmaType(MSPROF_HCCL_INVALID_UINT) | ||
| 693 | - { | ||
| 694 | - } | ||
| 695 | - | ||
| 696 | -}; | ||
| 697 | - | ||
| 698 | -struct MsprofAicpuMC2HcclInfo { | ||
| 699 | - uint64_t itemId; | ||
| 700 | - uint64_t cclTag; | ||
| 701 | - uint64_t groupName; | ||
| 702 | - uint32_t localRank; | ||
| 703 | - uint32_t remoteRank; | ||
| 704 | - uint32_t rankSize; | ||
| 705 | - uint32_t workFlowMode; | ||
| 706 | - uint32_t planeID; | ||
| 707 | - uint32_t ctxId; | ||
| 708 | - uint64_t notifyID; | ||
| 709 | - uint32_t stage; | ||
| 710 | - uint32_t role; // role {0: dst, 1:src} | ||
| 711 | - double durationEstimated; | ||
| 712 | - uint64_t srcAddr; | ||
| 713 | - uint64_t dstAddr; | ||
| 714 | - uint64_t dataSize; // bytes | ||
| 715 | - uint32_t opType; // {0: sum, 1: mul, 2: max, 3: min} | ||
| 716 | - uint32_t dataType; // data type {0: INT8, 1: INT16, 2: INT32, 3: FP16, 4:FP32, 5:INT64, 6:UINT64} | ||
| 717 | - uint32_t linkType; // link type {0: 'OnChip', 1: 'HCCS', 2: 'PCIe', 3: 'RoCE'} | ||
| 718 | - uint32_t transportType; // transport type {0: SDMA, 1: RDMA, 2:LOCAL} | ||
| 719 | - uint32_t rdmaType; // RDMA type {0: RDMASendNotify, 1:RDMASendPayload} | ||
| 720 | - uint32_t taskId; | ||
| 721 | - uint16_t streamId; | ||
| 722 | - uint16_t reserve[3]; | ||
| 723 | -}; | ||
| 724 | - | ||
| 725 | -struct ProfilingDeviceCommResInfo { | ||
| 726 | - uint64_t groupName; // 通信域 | ||
| 727 | - uint32_t rankSize; // 通信域内rank总数 | ||
| 728 | - uint32_t rankId; // 当前device rankId,通信域内编号 | ||
| 729 | - uint32_t usrRankId; // 当前device rankId,全局编号 | ||
| 730 | - uint32_t aicpuKfcStreamId; // MC2中launch aicpu kfc算子的stream | ||
| 731 | - uint32_t commStreamSize; // 当前device侧使用的通信stream数量 | ||
| 732 | - uint32_t commStreamIds[8]; // 具体streamId | ||
| 733 | - uint32_t reserve; | ||
| 734 | -}; | ||
| 735 | - | ||
| 736 | - | ||
| 737 | -struct MsprofMultiThread { | ||
| 738 | - uint32_t threadNum; | ||
| 739 | - uint32_t threadId[MSPROF_MULTI_THREAD_MAX_NUM]; | ||
| 740 | -}; | ||
| 741 | - | ||
| 742 | - | ||
| 743 | - | ||
| 744 | - | ||
| 745 | -struct MsprofNodeBasicInfo { | ||
| 746 | - uint64_t opName; | ||
| 747 | - uint32_t taskType; | ||
| 748 | - uint64_t opType; | ||
| 749 | - uint32_t blockDim; | ||
| 750 | - uint32_t opFlag; | ||
| 751 | -}; | ||
| 752 | - | ||
| 753 | -enum AttrType { | ||
| 754 | - OP_ATTR = 0, | ||
| 755 | -}; | ||
| 756 | - | ||
| 757 | -struct MsprofAttrInfo { | ||
| 758 | - uint64_t opName; | ||
| 759 | - uint32_t attrType; | ||
| 760 | - uint64_t hashId; | ||
| 761 | -}; | ||
| 762 | - | ||
| 763 | -struct MsrofTensorData { | ||
| 764 | - uint32_t tensorType; | ||
| 765 | - uint32_t format; | ||
| 766 | - uint32_t dataType; | ||
| 767 | - uint32_t shape[MSPROF_GE_TENSOR_DATA_SHAPE_LEN]; | ||
| 768 | -}; | ||
| 769 | - | ||
| 770 | -struct MsprofTensorInfo { | ||
| 771 | - uint64_t opName; | ||
| 772 | - uint32_t tensorNum; | ||
| 773 | - struct MsrofTensorData tensorData[MSPROF_GE_TENSOR_DATA_NUM]; | ||
| 774 | -}; | ||
| 775 | - | ||
| 776 | -struct MsprofHCCLOPInfo { // for MsprofReportCompactInfo buffer data | ||
| 777 | - uint8_t relay : 1; // 借轨通信 | ||
| 778 | - uint8_t retry : 1; // 重传标识 | ||
| 779 | - uint8_t dataType; // 跟HcclDataType类型保存一致 | ||
| 780 | - uint16_t algType; // algtype 每4位表示一个算法阶段 | ||
| 781 | - uint64_t count; // 发送数据个数 | ||
| 782 | - uint64_t groupName; // group hash id | ||
| 783 | -}; | ||
| 784 | - | ||
| 785 | -struct ProfFusionOpInfo { | ||
| 786 | -uint64_t opName; | ||
| 787 | -uint32_t fusionOpNum; | ||
| 788 | -uint64_t inputMemsize; | ||
| 789 | -uint64_t outputMemsize; | ||
| 790 | -uint64_t weightMemSize; | ||
| 791 | -uint64_t workspaceMemSize; | ||
| 792 | -uint64_t totalMemSize; | ||
| 793 | -uint64_t fusionOpId[MSPROF_GE_FUSION_OP_NUM]; | ||
| 794 | -}; | ||
| 795 | - | ||
| 796 | -struct MsprofContextIdInfo { | ||
| 797 | - uint64_t opName; | ||
| 798 | - uint32_t ctxIdNum; | ||
| 799 | - uint32_t ctxIds[MSPROF_CTX_ID_MAX_NUM]; | ||
| 800 | -}; | ||
| 801 | - | ||
| 802 | -struct MsprofGraphIdInfo { | ||
| 803 | - uint64_t modelName; | ||
| 804 | - uint32_t graphId; | ||
| 805 | - uint32_t modelId; | ||
| 806 | -}; | ||
| 807 | - | ||
| 808 | -struct MsprofMemoryInfo { | ||
| 809 | - uint64_t addr; | ||
| 810 | - int64_t size; | ||
| 811 | - uint64_t nodeId; // op name hash id | ||
| 812 | - uint64_t totalAllocateMemory; | ||
| 813 | - uint64_t totalReserveMemory; | ||
| 814 | - uint32_t deviceId; | ||
| 815 | - uint32_t deviceType; | ||
| 816 | -}; | ||
| 817 | - | ||
| 818 | -/** | ||
| 819 | - * @name MsprofStampInfo | ||
| 820 | - * @brief struct of data reported by msproftx | ||
| 821 | - */ | ||
| 822 | -struct MsprofStampInfo { | ||
| 823 | - uint16_t magicNumber; | ||
| 824 | - uint16_t dataTag; | ||
| 825 | - uint32_t processId; | ||
| 826 | - uint32_t threadId; | ||
| 827 | - uint32_t category; // marker category | ||
| 828 | - uint32_t eventType; | ||
| 829 | - int32_t payloadType; | ||
| 830 | - union PayloadValue { | ||
| 831 | - uint64_t ullValue; | ||
| 832 | - int64_t llValue; | ||
| 833 | - double dValue; | ||
| 834 | - uint32_t uiValue[2]; | ||
| 835 | - int32_t iValue[2]; | ||
| 836 | - float fValue[2]; | ||
| 837 | - } payload; // payload info for marker | ||
| 838 | - uint64_t startTime; | ||
| 839 | - uint64_t endTime; | ||
| 840 | - int32_t messageType; | ||
| 841 | - char message[128]; | ||
| 842 | -}; | ||
| 843 | - | ||
| 844 | -struct MsprofStaticOpMem { | ||
| 845 | - int64_t size; // op memory size | ||
| 846 | - uint64_t opName; // op name hash id | ||
| 847 | - uint64_t lifeStart; // serial number of op memory used | ||
| 848 | - uint64_t lifeEnd; // serial number of op memory used | ||
| 849 | - uint64_t totalAllocateMemory; // static graph total allocate memory | ||
| 850 | - uint64_t dynOpName; // 0: invalid, other: dynamic op name of root | ||
| 851 | - uint32_t graphId; // multipe model | ||
| 852 | -}; | ||
| 853 | - | ||
| 854 | - | ||
| 855 | -struct MsprofLogicStreamInfo { | ||
| 856 | - uint32_t logicStreamId; | ||
| 857 | - uint32_t physicStreamNum; | ||
| 858 | - uint32_t physicStreamId[MSPROF_PHYSIC_STREAM_ID_MAX_NUM]; | ||
| 859 | -}; | ||
| 860 | - | ||
| 861 | -struct MsprofExeomLoadInfo { | ||
| 862 | - uint32_t modelId; | ||
| 863 | - uint32_t reserve; | ||
| 864 | - uint64_t modelName; /* name hash */ | ||
| 865 | -}; | ||
| 866 | - | ||
| 867 | - | ||
| 868 | -struct MsprofApi { // for MsprofReportApi | ||
| 869 | - | ||
| 870 | - uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 871 | - | ||
| 872 | - uint16_t magicNumber; | ||
| 873 | - | ||
| 874 | - uint16_t level; | ||
| 875 | - uint32_t type; | ||
| 876 | - uint32_t threadId; | ||
| 877 | - uint32_t reserve; | ||
| 878 | - uint64_t beginTime; | ||
| 879 | - uint64_t endTime; | ||
| 880 | - uint64_t itemId; | ||
| 881 | -}; | ||
| 882 | - | ||
| 883 | -struct MsprofEvent { // for MsprofReportEvent | ||
| 884 | - | ||
| 885 | - uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 886 | - | ||
| 887 | - uint16_t magicNumber; | ||
| 888 | - | ||
| 889 | - uint16_t level; | ||
| 890 | - uint32_t type; | ||
| 891 | - uint32_t threadId; | ||
| 892 | - uint32_t requestId; // 0xFFFF means single event | ||
| 893 | - uint64_t timeStamp; | ||
| 894 | - | ||
| 895 | - uint64_t eventFlag = MSPROF_EVENT_FLAG; | ||
| 896 | - | ||
| 897 | - uint64_t eventFlag; | ||
| 898 | - | ||
| 899 | - uint64_t itemId; | ||
| 900 | -}; | ||
| 901 | - | ||
| 902 | -struct MsprofRuntimeTrack { // for MsprofReportCompactInfo buffer data | ||
| 903 | - uint16_t deviceId; | ||
| 904 | - uint16_t streamId; | ||
| 905 | - uint32_t taskId; | ||
| 906 | - uint64_t taskType; // task message hash id | ||
| 907 | -}; | ||
| 908 | - | ||
| 909 | -struct MsprofDpuTrack { // for MsprofReportCompactInfo buffer data | ||
| 910 | - uint16_t deviceId; // high 4 bits, devType: dpu: 1, low 12 bits device id | ||
| 911 | - uint16_t streamId; | ||
| 912 | - uint32_t taskId; | ||
| 913 | - uint32_t taskType; // task type enum | ||
| 914 | - uint32_t res; | ||
| 915 | - uint64_t startTime; // start time | ||
| 916 | -}; | ||
| 917 | - | ||
| 918 | - | ||
| 919 | -struct MsprofCompactInfo { // for MsprofReportCompactInfo buffer data | ||
| 920 | - | ||
| 921 | - uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 922 | - | ||
| 923 | - uint16_t magicNumber; | ||
| 924 | - | ||
| 925 | - uint16_t level; | ||
| 926 | - uint32_t type; | ||
| 927 | - uint32_t threadId; | ||
| 928 | - uint32_t dataLen; | ||
| 929 | - uint64_t timeStamp; | ||
| 930 | - union { | ||
| 931 | - uint8_t info[MSPROF_COMPACT_INFO_DATA_LENGTH]; | ||
| 932 | - struct MsprofRuntimeTrack runtimeTrack; | ||
| 933 | - struct MsprofNodeBasicInfo nodeBasicInfo; | ||
| 934 | - struct MsprofHCCLOPInfo hcclopInfo; | ||
| 935 | - struct MsprofDpuTrack dpuTack; | ||
| 936 | - } data; | ||
| 937 | -}; | ||
| 938 | - | ||
| 939 | - | ||
| 940 | -struct MsprofAdditionalInfo { // for MsprofReportAdditionalInfo buffer data | ||
| 941 | - | ||
| 942 | - uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 943 | - | ||
| 944 | - uint16_t magicNumber; | ||
| 945 | - | ||
| 946 | - uint16_t level; | ||
| 947 | - uint32_t type; | ||
| 948 | - uint32_t threadId; | ||
| 949 | - uint32_t dataLen; | ||
| 950 | - uint64_t timeStamp; | ||
| 951 | - uint8_t data[MSPROF_ADDTIONAL_INFO_DATA_LENGTH]; | ||
| 952 | -}; | ||
| 953 | - | ||
| 954 | -/** | ||
| 955 | - * @name MsprofErrorCode | ||
| 956 | - * @brief error code | ||
| 957 | - */ | ||
| 958 | -enum MsprofErrorCode { | ||
| 959 | - MSPROF_ERROR_NONE = 0, | ||
| 960 | - MSPROF_ERROR_MEM_NOT_ENOUGH, | ||
| 961 | - MSPROF_ERROR_GET_ENV, | ||
| 962 | - MSPROF_ERROR_CONFIG_INVALID, | ||
| 963 | - MSPROF_ERROR_ACL_JSON_OFF, | ||
| 964 | - MSPROF_ERROR, | ||
| 965 | - MSPROF_ERROR_UNINITIALIZE, | ||
| 966 | -}; | ||
| 967 | - | ||
| 968 | - | ||
| 969 | - | ||
| 970 | -/** | ||
| 971 | - * @name ReporterData | ||
| 972 | - * @brief struct of data to report | ||
| 973 | - */ | ||
| 974 | -struct ReporterData { | ||
| 975 | - char tag[MSPROF_ENGINE_MAX_TAG_LEN + 1]; // the sub-type of the module, data with different tag will be writen | ||
| 976 | - int32_t deviceId; // the index of device | ||
| 977 | - size_t dataLen; // the length of send data | ||
| 978 | - uint8_t *data; // the data content | ||
| 979 | -}; | ||
| 980 | - | ||
| 981 | -/** | ||
| 982 | - * @name MsprofHashData | ||
| 983 | - * @brief struct of data to hash | ||
| 984 | - */ | ||
| 985 | -struct MsprofHashData { | ||
| 986 | - int32_t deviceId; // the index of device | ||
| 987 | - size_t dataLen; // the length of data | ||
| 988 | - uint8_t *data; // the data content | ||
| 989 | - uint64_t hashId; // the id of hashed data | ||
| 990 | -}; | ||
| 991 | - | ||
| 992 | -enum MsprofConfigParamType { | ||
| 993 | - DEV_CHANNEL_RESOURCE = 0, // device channel resource | ||
| 994 | - HELPER_HOST_SERVER // helper host server | ||
| 995 | -}; | ||
| 996 | - | ||
| 997 | -/** | ||
| 998 | - * @name MsprofConfigParam | ||
| 999 | - * @brief struct of set config | ||
| 1000 | - */ | ||
| 1001 | -struct MsprofConfigParam { | ||
| 1002 | - uint32_t deviceId; // the index of device | ||
| 1003 | - uint32_t type; // DEV_CHANNEL_RESOURCE; HELPER_HOST_SERVER | ||
| 1004 | - uint32_t value; // DEV_CHANNEL_RESOURCE: 1 off; HELPER_HOST_SERVER: 1 on | ||
| 1005 | -}; | ||
| 1006 | - | ||
| 1007 | -/** | ||
| 1008 | - * @name MsprofReporterModuleId | ||
| 1009 | - * @brief module id of data to report | ||
| 1010 | - */ | ||
| 1011 | -enum MsprofReporterModuleId { | ||
| 1012 | - MSPROF_MODULE_DATA_PREPROCESS = 0, // DATA_PREPROCESS | ||
| 1013 | - MSPROF_MODULE_HCCL, // HCCL | ||
| 1014 | - MSPROF_MODULE_ACL, // AclModule | ||
| 1015 | - MSPROF_MODULE_FRAMEWORK, // Framework | ||
| 1016 | - MSPROF_MODULE_RUNTIME, // runtime | ||
| 1017 | - MSPROF_MODULE_MSPROF // msprofTx | ||
| 1018 | -}; | ||
| 1019 | - | ||
| 1020 | -/** | ||
| 1021 | - * @name MsprofReporterCallbackType | ||
| 1022 | - * @brief reporter callback request type | ||
| 1023 | - */ | ||
| 1024 | -enum MsprofReporterCallbackType { | ||
| 1025 | - MSPROF_REPORTER_REPORT = 0, // report data | ||
| 1026 | - MSPROF_REPORTER_INIT, // init reporter | ||
| 1027 | - MSPROF_REPORTER_UNINIT, // uninit reporter | ||
| 1028 | - MSPROF_REPORTER_DATA_MAX_LEN, // data max length for calling report callback | ||
| 1029 | - MSPROF_REPORTER_HASH // hash data to id | ||
| 1030 | -}; | ||
| 1031 | - | ||
| 1032 | - | ||
| 1033 | - | ||
| 1034 | -/** | ||
| 1035 | - * @name MsprofGeOptions | ||
| 1036 | - * @brief struct of MSPROF_CTRL_INIT_GE_OPTIONS | ||
| 1037 | - */ | ||
| 1038 | -struct MsprofGeOptions { | ||
| 1039 | - char jobId[MSPROF_OPTIONS_DEF_LEN_MAX]; | ||
| 1040 | - char options[MSPROF_OPTIONS_DEF_LEN_MAX]; | ||
| 1041 | -}; | ||
| 1042 | - | ||
| 1043 | -/** | ||
| 1044 | - * @name MsprofCtrlCallbackType | ||
| 1045 | - * @brief ctrl callback request type | ||
| 1046 | - */ | ||
| 1047 | -enum MsprofCtrlCallbackType { | ||
| 1048 | - MSPROF_CTRL_INIT_ACL_ENV = 0, // start profiling with acl env | ||
| 1049 | - MSPROF_CTRL_INIT_ACL_JSON = 1, // start pro with acl.json | ||
| 1050 | - MSPROF_CTRL_INIT_GE_OPTIONS = 2, // start profiling with ge env and options | ||
| 1051 | - MSPROF_CTRL_FINALIZE = 3, // stop profiling | ||
| 1052 | - MSPROF_CTRL_INIT_HELPER = 4, // start profiling in helper device | ||
| 1053 | - MSPROF_CTRL_INIT_PURE_CPU = 5, // start profiling in pure cpu | ||
| 1054 | - MSPROF_CTRL_INIT_DYNA = 0xFF, // start profiling for dynamic profiling | ||
| 1055 | -}; | ||
| 1056 | - | ||
| 1057 | -enum MsprofCommandHandleType { | ||
| 1058 | - PROF_COMMANDHANDLE_TYPE_INIT = 0, | ||
| 1059 | - PROF_COMMANDHANDLE_TYPE_START, | ||
| 1060 | - PROF_COMMANDHANDLE_TYPE_STOP, | ||
| 1061 | - PROF_COMMANDHANDLE_TYPE_FINALIZE, | ||
| 1062 | - PROF_COMMANDHANDLE_TYPE_MODEL_SUBSCRIBE, | ||
| 1063 | - PROF_COMMANDHANDLE_TYPE_MODEL_UNSUBSCRIBE, | ||
| 1064 | - PROF_COMMANDHANDLE_TYPE_MAX | ||
| 1065 | -}; | ||
| 1066 | - | ||
| 1067 | -enum MsprofConfigType { | ||
| 1068 | - MSPROF_CONFIG_HELPER_HOST = 0 | ||
| 1069 | -}; | ||
| 1070 | - | ||
| 1071 | -/** | ||
| 1072 | - * @brief profiling command type | ||
| 1073 | - */ | ||
| 1074 | -enum ProfCtrlType { | ||
| 1075 | - PROF_CTRL_INVALID = 0, | ||
| 1076 | - PROF_CTRL_SWITCH, | ||
| 1077 | - PROF_CTRL_REPORTER, | ||
| 1078 | - PROF_CTRL_STEPINFO, | ||
| 1079 | - PROF_CTRL_BUTT | ||
| 1080 | -}; | ||
| 1081 | - | ||
| 1082 | -/** | ||
| 1083 | - * @brief Prof Chip ID | ||
| 1084 | - */ | ||
| 1085 | -enum Prof_Chip_ID { | ||
| 1086 | - PROF_CHIP_ID0 = 0 | ||
| 1087 | -}; | ||
| 1088 | - | ||
| 1089 | -/** | ||
| 1090 | - * @brief the struct of profiling set setp info | ||
| 1091 | - */ | ||
| 1092 | -typedef struct ProfStepInfoCmd { | ||
| 1093 | - uint64_t index_id; | ||
| 1094 | - uint16_t tag_id; | ||
| 1095 | - void *stream; | ||
| 1096 | -} ProfStepInfoCmd_t; | ||
| 1097 | - | ||
| 1098 | - | ||
| 1099 | -} | ||
| 1100 | - | ||
| 1101 | - | ||
| @@ -1,99 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * @file prof_data_config.h | ||
| 3 | - * | ||
| 4 | - * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. | ||
| 5 | - * | ||
| 6 | - * This program is distributed in the hope that it will be useful, | ||
| 7 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 8 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | - * | ||
| 10 | - */ | ||
| 11 | - | ||
| 12 | - | ||
| 13 | - | ||
| 14 | - | ||
| 15 | -// DataTypeConfig | ||
| 16 | - | ||
| 17 | - | ||
| 18 | - | ||
| 19 | - | ||
| 20 | - | ||
| 21 | - | ||
| 22 | - | ||
| 23 | - | ||
| 24 | - | ||
| 25 | - | ||
| 26 | - | ||
| 27 | - | ||
| 28 | - | ||
| 29 | - | ||
| 30 | - | ||
| 31 | - | ||
| 32 | -// system profilinig switch | ||
| 33 | - | ||
| 34 | - | ||
| 35 | - | ||
| 36 | - | ||
| 37 | - | ||
| 38 | - | ||
| 39 | - | ||
| 40 | - | ||
| 41 | - | ||
| 42 | - | ||
| 43 | - | ||
| 44 | - | ||
| 45 | - | ||
| 46 | - | ||
| 47 | - | ||
| 48 | - | ||
| 49 | - | ||
| 50 | - | ||
| 51 | - | ||
| 52 | - | ||
| 53 | - | ||
| 54 | - | ||
| 55 | - | ||
| 56 | -// DataTypeConfig MASK | ||
| 57 | - | ||
| 58 | - | ||
| 59 | - | ||
| 60 | - | ||
| 61 | - | ||
| 62 | - | ||
| 63 | - | ||
| 64 | - | ||
| 65 | - | ||
| 66 | - | ||
| 67 | - | ||
| 68 | - | ||
| 69 | - | ||
| 70 | - | ||
| 71 | - | ||
| 72 | - | ||
| 73 | - | ||
| 74 | -// system profilinig mask | ||
| 75 | - | ||
| 76 | - | ||
| 77 | - | ||
| 78 | - | ||
| 79 | - | ||
| 80 | - | ||
| 81 | - | ||
| 82 | - | ||
| 83 | - | ||
| 84 | - | ||
| 85 | - | ||
| 86 | - | ||
| 87 | - | ||
| 88 | - | ||
| 89 | - | ||
| 90 | - | ||
| 91 | - | ||
| 92 | - | ||
| 93 | - | ||
| 94 | - | ||
| 95 | - | ||
| 96 | -// devprof config | ||
| 97 | - | ||
| 98 | - | ||
| 99 | - | ||
| @@ -0,0 +1,180 @@ | |||
| 1 | +/** | ||
| 2 | + * @file aprof_pub.h | ||
| 3 | + * | ||
| 4 | + * Minimal profiling declarations required by DVM and MLIR launcher code. | ||
| 5 | + */ | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +extern "C" { | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +typedef void *VOID_PTR; | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + | ||
| 43 | +enum MsprofErrorCode { | ||
| 44 | + MSPROF_ERROR_NONE = 0, | ||
| 45 | +}; | ||
| 46 | + | ||
| 47 | +enum MsprofCommandHandleType { | ||
| 48 | + PROF_COMMANDHANDLE_TYPE_INIT = 0, | ||
| 49 | + PROF_COMMANDHANDLE_TYPE_START, | ||
| 50 | + PROF_COMMANDHANDLE_TYPE_STOP, | ||
| 51 | + PROF_COMMANDHANDLE_TYPE_FINALIZE, | ||
| 52 | + PROF_COMMANDHANDLE_TYPE_MODEL_SUBSCRIBE, | ||
| 53 | + PROF_COMMANDHANDLE_TYPE_MODEL_UNSUBSCRIBE, | ||
| 54 | + PROF_COMMANDHANDLE_TYPE_MAX | ||
| 55 | +}; | ||
| 56 | + | ||
| 57 | +enum MsprofGeTaskType { | ||
| 58 | + MSPROF_GE_TASK_TYPE_AI_CORE = 0, | ||
| 59 | +}; | ||
| 60 | + | ||
| 61 | +enum MsprofGeTensorType { | ||
| 62 | + MSPROF_GE_TENSOR_TYPE_INPUT = 0, | ||
| 63 | + MSPROF_GE_TENSOR_TYPE_OUTPUT, | ||
| 64 | +}; | ||
| 65 | + | ||
| 66 | +enum ProfCtrlType { | ||
| 67 | + PROF_CTRL_INVALID = 0, | ||
| 68 | + PROF_CTRL_SWITCH, | ||
| 69 | +}; | ||
| 70 | + | ||
| 71 | +struct MsprofCommandHandleParams { | ||
| 72 | + uint32_t pathLen; | ||
| 73 | + uint32_t storageLimit; | ||
| 74 | + uint32_t profDataLen; | ||
| 75 | + char path[PATH_LEN_MAX + 1]; | ||
| 76 | + char profData[PARAM_LEN_MAX + 1]; | ||
| 77 | +}; | ||
| 78 | + | ||
| 79 | +struct MsprofCommandHandle { | ||
| 80 | + uint64_t profSwitch; | ||
| 81 | + uint64_t profSwitchHi; | ||
| 82 | + uint32_t devNums; | ||
| 83 | + uint32_t devIdList[MSPROF_MAX_DEV_NUM]; | ||
| 84 | + uint32_t modelId; | ||
| 85 | + uint32_t type; | ||
| 86 | + uint32_t cacheFlag; | ||
| 87 | + struct MsprofCommandHandleParams params; | ||
| 88 | +}; | ||
| 89 | + | ||
| 90 | + | ||
| 91 | + | ||
| 92 | +struct MsprofNodeBasicInfo { | ||
| 93 | + uint64_t opName; | ||
| 94 | + uint32_t taskType; | ||
| 95 | + uint64_t opType; | ||
| 96 | + uint32_t blockDim; | ||
| 97 | + uint32_t opFlag; | ||
| 98 | +}; | ||
| 99 | + | ||
| 100 | +struct MsrofTensorData { | ||
| 101 | + uint32_t tensorType; | ||
| 102 | + uint32_t format; | ||
| 103 | + uint32_t dataType; | ||
| 104 | + uint32_t shape[MSPROF_GE_TENSOR_DATA_SHAPE_LEN]; | ||
| 105 | +}; | ||
| 106 | + | ||
| 107 | +struct MsprofTensorInfo { | ||
| 108 | + uint64_t opName; | ||
| 109 | + uint32_t tensorNum; | ||
| 110 | + struct MsrofTensorData tensorData[MSPROF_GE_TENSOR_DATA_NUM]; | ||
| 111 | +}; | ||
| 112 | + | ||
| 113 | +struct MsprofContextIdInfo { | ||
| 114 | + uint64_t opName; | ||
| 115 | + uint32_t ctxIdNum; | ||
| 116 | + uint32_t ctxIds[MSPROF_CTX_ID_MAX_NUM]; | ||
| 117 | +}; | ||
| 118 | + | ||
| 119 | + | ||
| 120 | + | ||
| 121 | +struct MsprofApi { | ||
| 122 | + | ||
| 123 | + uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 124 | + | ||
| 125 | + uint16_t magicNumber; | ||
| 126 | + | ||
| 127 | + uint16_t level; | ||
| 128 | + uint32_t type; | ||
| 129 | + uint32_t threadId; | ||
| 130 | + uint32_t reserve; | ||
| 131 | + uint64_t beginTime; | ||
| 132 | + uint64_t endTime; | ||
| 133 | + uint64_t itemId; | ||
| 134 | +}; | ||
| 135 | + | ||
| 136 | +struct MsprofCompactInfo { | ||
| 137 | + | ||
| 138 | + uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 139 | + | ||
| 140 | + uint16_t magicNumber; | ||
| 141 | + | ||
| 142 | + uint16_t level; | ||
| 143 | + uint32_t type; | ||
| 144 | + uint32_t threadId; | ||
| 145 | + uint32_t dataLen; | ||
| 146 | + uint64_t timeStamp; | ||
| 147 | + union { | ||
| 148 | + uint8_t info[MSPROF_COMPACT_INFO_DATA_LENGTH]; | ||
| 149 | + struct MsprofNodeBasicInfo nodeBasicInfo; | ||
| 150 | + } data; | ||
| 151 | +}; | ||
| 152 | + | ||
| 153 | +struct MsprofAdditionalInfo { | ||
| 154 | + | ||
| 155 | + uint16_t magicNumber = MSPROF_REPORT_DATA_MAGIC_NUM; | ||
| 156 | + | ||
| 157 | + uint16_t magicNumber; | ||
| 158 | + | ||
| 159 | + uint16_t level; | ||
| 160 | + uint32_t type; | ||
| 161 | + uint32_t threadId; | ||
| 162 | + uint32_t dataLen; | ||
| 163 | + uint64_t timeStamp; | ||
| 164 | + uint8_t data[MSPROF_ADDTIONAL_INFO_DATA_LENGTH]; | ||
| 165 | +}; | ||
| 166 | + | ||
| 167 | +typedef int32_t (*ProfCommandHandle)(uint32_t type, void *data, uint32_t len); | ||
| 168 | + | ||
| 169 | +MSVP_PROF_API int32_t MsprofRegisterCallback(uint32_t moduleId, ProfCommandHandle handle); | ||
| 170 | +MSVP_PROF_API int32_t MsprofReportApi(uint32_t nonPersistantFlag, const struct MsprofApi *api); | ||
| 171 | +MSVP_PROF_API int32_t MsprofReportCompactInfo(uint32_t nonPersistantFlag, const VOID_PTR data, uint32_t length); | ||
| 172 | +MSVP_PROF_API int32_t MsprofReportAdditionalInfo(uint32_t nonPersistantFlag, const VOID_PTR data, uint32_t length); | ||
| 173 | +MSVP_PROF_API uint64_t MsprofGetHashId(const char *hashInfo, size_t length); | ||
| 174 | +MSVP_PROF_API uint64_t MsprofSysCycleTime(void); | ||
| 175 | + | ||
| 176 | + | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | + | ||
| 180 | + | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | +/** | ||
| 2 | + * @file prof_api.h | ||
| 3 | + * | ||
| 4 | + * Minimal profiling API declarations required by DVM and MLIR launcher code. | ||
| 5 | + */ | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | +/** | ||
| 2 | + * @file prof_common.h | ||
| 3 | + * | ||
| 4 | + * Minimal common profiling declarations required by DVM and MLIR launcher code. | ||
| 5 | + */ | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +extern "C" { | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +typedef const void *ConstVoidPtr; | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | +set(DVM_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/dvm) | ||
| 2 | +set(DVM_LIB ${DVM_ROOT}/libdvm.a) | ||
| 3 | + | ||
| 4 | +set(ACL_INC_ROOT "${TORCHNPU_THIRD_PARTY_ROOT}/acl/inc") | ||
| 5 | +set(DVM_MAKE_ARGS | ||
| 6 | + PRE_ASCEND=1 | ||
| 7 | + DVM_CUSTOM_FLAGS=-I${ACL_INC_ROOT} | ||
| 8 | +) | ||
| 9 | + | ||
| 10 | +file(GLOB DVM_DEPENDS | ||
| 11 | + CONFIGURE_DEPENDS | ||
| 12 | + "${DVM_ROOT}/Makefile" | ||
| 13 | + "${DVM_ROOT}/env.sh" | ||
| 14 | + "${DVM_ROOT}/src/*.cc" | ||
| 15 | + "${DVM_ROOT}/src/*.h" | ||
| 16 | + "${DVM_ROOT}/include/*.h" | ||
| 17 | +) | ||
| 18 | + | ||
| 19 | +add_custom_command( | ||
| 20 | + OUTPUT ${DVM_LIB} | ||
| 21 | + COMMAND make -C ${DVM_ROOT} ${DVM_MAKE_ARGS} libdvm.a | ||
| 22 | + WORKING_DIRECTORY ${DVM_ROOT} | ||
| 23 | + DEPENDS ${DVM_DEPENDS} | ||
| 24 | + COMMENT "Building DVM static library" | ||
| 25 | + VERBATIM | ||
| 26 | +) | ||
| 27 | + | ||
| 28 | +add_custom_target(dvm_build DEPENDS ${DVM_LIB}) | ||
| 29 | +add_library(dvm STATIC IMPORTED GLOBAL) | ||
| 30 | +set_target_properties(dvm PROPERTIES IMPORTED_LOCATION ${DVM_LIB}) | ||
| 31 | +add_dependencies(dvm dvm_build) | ||
| @@ -32,6 +32,19 @@ def _load_mlir_backend(): | |||
| 32 | apply_mlir_inductor_patch() | 32 | apply_mlir_inductor_patch() |
| 33 | register_mlir_codegen_backend() | 33 | register_mlir_codegen_backend() |
| 34 | 34 | ||
| 35 | + | ||
| 36 | +def _load_dvm_backend(): | ||
| 37 | + import torch | ||
| 38 | + from .ascend_npu_ir.ascend_npu_ir.npu import npu_inductor_plugin | ||
| 39 | + from .lowering_patch import apply_mlir_inductor_patch | ||
| 40 | + from .ascend_npu_ir.ascend_npu_ir.npu.npu_inductor_plugin import ( | ||
| 41 | + register_mlir_codegen_backend, | ||
| 42 | + ) | ||
| 43 | + apply_mlir_inductor_patch() | ||
| 44 | + register_mlir_codegen_backend() | ||
| 45 | + from .dvm import mlir_fusion | ||
| 46 | + | ||
| 47 | + | ||
| 35 | def _load_triton_backend(): | 48 | def _load_triton_backend(): |
| 36 | import os | 49 | import os |
| 37 | import torch | 50 | import torch |
| @@ -158,6 +171,7 @@ def _load_triton_backend(): | |||
| 158 | 171 | ||
| 159 | _BACKEND_LOADERS = { | 172 | _BACKEND_LOADERS = { |
| 160 | "mlir": _load_mlir_backend, | 173 | "mlir": _load_mlir_backend, |
| 174 | + "dvm": _load_dvm_backend, | ||
| 161 | "default": _load_triton_backend, | 175 | "default": _load_triton_backend, |
| 162 | } | 176 | } |
| 163 | 177 | ||
| @@ -218,7 +218,6 @@ class CustomAsyncCompile(AsyncCompile): | |||
| 218 | assert get_compile_threads() > 1 | 218 | assert get_compile_threads() > 1 |
| 219 | # Wrapper around ProcessPoolExecutor forks in a new process we control | 219 | # Wrapper around ProcessPoolExecutor forks in a new process we control |
| 220 | log.info("Creating subprocess pool with %d workers", get_compile_threads()) | 220 | log.info("Creating subprocess pool with %d workers", get_compile_threads()) |
| 221 | - os.environ['TORCHINDUCTOR_NPU_BACKEND'] = 'mlir' | ||
| 222 | pool = SubprocPool(get_compile_threads()) | 221 | pool = SubprocPool(get_compile_threads()) |
| 223 | 222 | ||
| 224 | # Set an attribute we can check to see if the pool is ready. | 223 | # Set an attribute we can check to see if the pool is ready. |
| @@ -32,8 +32,6 @@ akg_spec = importlib.util.find_spec("akg") | |||
| 32 | if akg_spec is not None: | 32 | if akg_spec is not None: |
| 33 | from akg.kernel import Kernel as MlirKernel | 33 | from akg.kernel import Kernel as MlirKernel |
| 34 | 34 | ||
| 35 | -os.environ['TORCHINDUCTOR_NPU_BACKEND'] = 'mlir' | ||
| 36 | - | ||
| 37 | 35 | ||
| 38 | reinterpret_tensor = torch.ops.inductor._reinterpret_tensor | 36 | reinterpret_tensor = torch.ops.inductor._reinterpret_tensor |
| 39 | global_cache = set() | 37 | global_cache = set() |
| @@ -0,0 +1,198 @@ | |||
| 1 | +import os | ||
| 2 | +import sys | ||
| 3 | +from functools import partial, wraps | ||
| 4 | + | ||
| 5 | +import torch | ||
| 6 | + | ||
| 7 | +import torch_npu | ||
| 8 | +from torch_npu._C.dvm import ( | ||
| 9 | + NDObject, | ||
| 10 | + DataType, | ||
| 11 | + DynKernel, | ||
| 12 | + GraphSplitKernel, | ||
| 13 | + DynGraphSplitKernel, | ||
| 14 | + TorchKernel as Kernel, | ||
| 15 | +) | ||
| 16 | +from torch_npu._inductor.npu_compare import check_accuracy_dvm | ||
| 17 | +from torch_npu.npu._backends import get_soc_version | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +debug_mode = os.environ.get("INDUCTOR_DVM_DEBUG_MODE", "0") == "1" | ||
| 21 | +bf16_vector_keep_promoted = False | ||
| 22 | + | ||
| 23 | +bool_ = DataType.bool | ||
| 24 | +float16 = DataType.float16 | ||
| 25 | +bfloat16 = DataType.bfloat16 | ||
| 26 | +float32 = DataType.float32 | ||
| 27 | +int32 = DataType.int32 | ||
| 28 | +int64 = DataType.int64 | ||
| 29 | + | ||
| 30 | +Ascend910B1 = 220 | ||
| 31 | +Ascend310B1 = 240 | ||
| 32 | +Ascend910_9391 = 250 | ||
| 33 | +Ascend950 = 260 | ||
| 34 | +is_ascend950 = get_soc_version() >= Ascend950 | ||
| 35 | + | ||
| 36 | +KERNEL_FACTORY = { | ||
| 37 | + ("mix", True): partial(DynKernel, Kernel.K_MIX, Kernel.F_DYN), | ||
| 38 | + ("mix", False): partial(Kernel, Kernel.K_MIX, 0), | ||
| 39 | + ("split", True): DynGraphSplitKernel, | ||
| 40 | + ("split", False): GraphSplitKernel, | ||
| 41 | + ("spec", True): partial( | ||
| 42 | + DynKernel, Kernel.K_VEC, Kernel.F_DYN | Kernel.F_SPEC | ||
| 43 | + ), | ||
| 44 | + ("spec", False): partial(Kernel, Kernel.K_VEC, Kernel.F_SPEC), | ||
| 45 | + ("vector", True): partial(DynKernel, Kernel.K_VEC, Kernel.F_DYN), | ||
| 46 | + ("vector", False): partial(Kernel, Kernel.K_VEC, 0), | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | + | ||
| 50 | +def kernel( | ||
| 51 | + ktype: str = "split", | ||
| 52 | + dyn_shape: bool = False, | ||
| 53 | +): | ||
| 54 | + r"""kernel(ktype="split", dyn_shape=False) | ||
| 55 | + | ||
| 56 | + Return a decorator that builds and executes a DVM kernel. | ||
| 57 | + | ||
| 58 | + The decorated function ``builder(kobj)`` constructs the kernel object. | ||
| 59 | + The returned callable supports two execution styles: | ||
| 60 | + | ||
| 61 | + - ``fn(*args, **kwargs)``: takes input tensors only and returns output tensor(s). | ||
| 62 | + - ``fn.run(*args, **kwargs)``: takes input and output tensors, writes results | ||
| 63 | + into the provided output buffers, and returns ``None``. | ||
| 64 | + | ||
| 65 | + Args: | ||
| 66 | + ktype (str, optional): kernel type. Default: ``"split"``. | ||
| 67 | + dyn_shape (bool, optional): enable dynamic shapes. Default: ``False``. | ||
| 68 | + | ||
| 69 | + Returns: | ||
| 70 | + Callable: a callable kernel wrapper with a ``run`` method and ``kobj`` attribute. | ||
| 71 | + """ | ||
| 72 | + | ||
| 73 | + def decorate(builder): | ||
| 74 | + kobj = KERNEL_FACTORY[(ktype, dyn_shape)]() | ||
| 75 | + kernel_name = getattr(builder, "__name__", "<unknown>") | ||
| 76 | + | ||
| 77 | + builder(kobj) | ||
| 78 | + kobj.setup() | ||
| 79 | + | ||
| 80 | + def _format_args(args): | ||
| 81 | + dump_parts = [] | ||
| 82 | + arg_summaries = [] | ||
| 83 | + for a in args: | ||
| 84 | + if isinstance(a, torch.Tensor): | ||
| 85 | + shape = tuple(a.shape) | ||
| 86 | + dump_parts.append(str(a.shape)) | ||
| 87 | + arg_summaries.append( | ||
| 88 | + f"Tensor(shape={shape}, dtype={a.dtype}, device={a.device})" | ||
| 89 | + ) | ||
| 90 | + elif isinstance(a, (torch.SymInt, torch.SymFloat)): | ||
| 91 | + sym_name = type(a).__name__ | ||
| 92 | + dump_parts.append(sym_name) | ||
| 93 | + arg_summaries.append(sym_name) | ||
| 94 | + else: | ||
| 95 | + arg_summaries.append(type(a).__name__) | ||
| 96 | + return dump_parts, arg_summaries | ||
| 97 | + | ||
| 98 | + def _post_run(args): | ||
| 99 | + try: | ||
| 100 | + torch_npu.npu.synchronize() | ||
| 101 | + except Exception as exc: | ||
| 102 | + dump_text = kobj.dump() | ||
| 103 | + das_text = kobj.das() | ||
| 104 | + dump_parts, arg_summaries = _format_args(args) | ||
| 105 | + dump_id = ",".join(dump_parts) | ||
| 106 | + msg = [ | ||
| 107 | + "DVM debug sync failed.", | ||
| 108 | + f"kernel_name={kernel_name}", | ||
| 109 | + f"dump_id={dump_id}", | ||
| 110 | + f"args={arg_summaries}", | ||
| 111 | + f"dump={dump_text}", | ||
| 112 | + f"das={das_text}", | ||
| 113 | + f"error={type(exc).__name__}: {exc}", | ||
| 114 | + ] | ||
| 115 | + print("\n".join(msg), file=sys.stderr) | ||
| 116 | + raise | ||
| 117 | + | ||
| 118 | + | ||
| 119 | + def fn(*args, **kwargs): | ||
| 120 | + outputs = kobj(*args) | ||
| 121 | + if debug_mode: | ||
| 122 | + _post_run(args) | ||
| 123 | + return outputs | ||
| 124 | + | ||
| 125 | + def run(*args, **kwargs): | ||
| 126 | + kobj.run(*args) | ||
| 127 | + if debug_mode: | ||
| 128 | + _post_run(args) | ||
| 129 | + | ||
| 130 | + fn.run = run | ||
| 131 | + fn.kobj = kobj | ||
| 132 | + | ||
| 133 | + return fn | ||
| 134 | + | ||
| 135 | + return decorate | ||
| 136 | + | ||
| 137 | + | ||
| 138 | +def _install_bf16_promote(): | ||
| 139 | + unsupported_bf16_ops = ( | ||
| 140 | + "sqrt", | ||
| 141 | + "abs", | ||
| 142 | + "log", | ||
| 143 | + "exp", | ||
| 144 | + "reciprocal", | ||
| 145 | + "logical_not", | ||
| 146 | + "round", | ||
| 147 | + "floor", | ||
| 148 | + "ceil", | ||
| 149 | + "trunc", | ||
| 150 | + "equal", | ||
| 151 | + "not_equal", | ||
| 152 | + "greater", | ||
| 153 | + "greater_equal", | ||
| 154 | + "less", | ||
| 155 | + "less_equal", | ||
| 156 | + "add", | ||
| 157 | + "sub", | ||
| 158 | + "mul", | ||
| 159 | + "div", | ||
| 160 | + "pow", | ||
| 161 | + "maximum", | ||
| 162 | + "minimum", | ||
| 163 | + "logical_and", | ||
| 164 | + "logical_or", | ||
| 165 | + "select", | ||
| 166 | + "sum", | ||
| 167 | + "max", | ||
| 168 | + "min", | ||
| 169 | + ) | ||
| 170 | + | ||
| 171 | + def _promote_bf16(op_fn): | ||
| 172 | + | ||
| 173 | + def wrapper(self, *args): | ||
| 174 | + need_cast_back = False | ||
| 175 | + | ||
| 176 | + def maybe_cast_arg(x): | ||
| 177 | + nonlocal need_cast_back | ||
| 178 | + if isinstance(x, NDObject) and x.dtype() == bfloat16: | ||
| 179 | + need_cast_back = True | ||
| 180 | + return self.cast(x, float32) | ||
| 181 | + return x | ||
| 182 | + | ||
| 183 | + new_args = tuple(maybe_cast_arg(a) for a in args) | ||
| 184 | + out = op_fn(self, *new_args) | ||
| 185 | + if bf16_vector_keep_promoted and need_cast_back : | ||
| 186 | + out = self.cast(out, bfloat16) | ||
| 187 | + return out | ||
| 188 | + | ||
| 189 | + return wrapper | ||
| 190 | + | ||
| 191 | + for name in unsupported_bf16_ops: | ||
| 192 | + op_fn = getattr(Kernel, name, None) | ||
| 193 | + if op_fn is not None: | ||
| 194 | + setattr(Kernel, name, _promote_bf16(op_fn)) | ||
| 195 | + | ||
| 196 | + | ||
| 197 | +if not is_ascend950: | ||
| 198 | + _install_bf16_promote() | ||
| @@ -0,0 +1,171 @@ | |||
| 1 | +import math | ||
| 2 | + | ||
| 3 | +import torch | ||
| 4 | +from torch._decomp import remove_decompositions | ||
| 5 | +from torch._inductor import decomposition as inductor_decomp | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +aten = torch.ops.aten | ||
| 9 | +prims = torch.ops.prims | ||
| 10 | +quantized = torch.ops.quantized | ||
| 11 | + | ||
| 12 | +decomps_to_exclude_npu = [ | ||
| 13 | + aten._batch_norm_no_update, | ||
| 14 | + aten._batch_norm_with_update, | ||
| 15 | + aten._batch_norm_with_update_functional, | ||
| 16 | + aten._log_softmax, | ||
| 17 | + aten._log_softmax_backward_data, | ||
| 18 | + aten.batch_norm_backward, | ||
| 19 | + aten.convolution_backward, | ||
| 20 | + aten.embedding, | ||
| 21 | + aten.embedding_backward, | ||
| 22 | + aten.embedding_dense_backward, | ||
| 23 | + aten.gelu.default, | ||
| 24 | + aten.gelu_backward.default, | ||
| 25 | + aten.grid_sampler_2d, | ||
| 26 | + aten.grid_sampler_2d_backward, | ||
| 27 | + aten.linalg_vector_norm, | ||
| 28 | + aten.max_pool2d_with_indices, | ||
| 29 | + aten.max_pool2d_with_indices_backward, | ||
| 30 | + aten.native_batch_norm, | ||
| 31 | + aten.native_group_norm, | ||
| 32 | + aten.nll_loss2d_backward, | ||
| 33 | + aten.nll_loss2d_forward, | ||
| 34 | + aten.nll_loss_backward, | ||
| 35 | + aten.nll_loss_forward, | ||
| 36 | + aten.reflection_pad2d, | ||
| 37 | + aten.reflection_pad2d_backward, | ||
| 38 | + aten.slice.Tensor, | ||
| 39 | + aten.triu, | ||
| 40 | + aten.upsample_bilinear2d, | ||
| 41 | + aten.upsample_bilinear2d_backward, | ||
| 42 | + aten.upsample_nearest1d, | ||
| 43 | + aten.upsample_nearest1d_backward, | ||
| 44 | + aten.upsample_nearest2d, | ||
| 45 | + aten.upsample_nearest2d_backward, | ||
| 46 | + aten.upsample_nearest3d, | ||
| 47 | + aten.upsample_nearest3d_backward, | ||
| 48 | + torch.ops.npu.npu_rotary_mul, | ||
| 49 | + torch.ops.npu.npu_rotary_mul_backward, | ||
| 50 | +] | ||
| 51 | + | ||
| 52 | +FP32_MIN_V2 = -8.8 | ||
| 53 | +FP32_MAX_V2 = 8.8 | ||
| 54 | +DOUBLE_X = 2.0 | ||
| 55 | + | ||
| 56 | + | ||
| 57 | +def tanh(a): | ||
| 58 | + """ | ||
| 59 | + y = (exp(2x) - 1) / (exp(2x) + 1) | ||
| 60 | + with x clipped to [-8.8, 8.8] in float32 before multiply-by-2. | ||
| 61 | + """ | ||
| 62 | + orig_dtype = a.dtype | ||
| 63 | + if orig_dtype != torch.float32: | ||
| 64 | + a = a.to(torch.float32) | ||
| 65 | + x = torch.clamp(a, min=FP32_MIN_V2, max=FP32_MAX_V2) | ||
| 66 | + x2 = x * DOUBLE_X | ||
| 67 | + e2x = torch.exp(x2) | ||
| 68 | + out = (e2x - 1.0) / (e2x + 1.0) | ||
| 69 | + | ||
| 70 | + if orig_dtype != torch.float32: | ||
| 71 | + out = out.to(orig_dtype) | ||
| 72 | + return out | ||
| 73 | + | ||
| 74 | + | ||
| 75 | +def gelu(a: torch.Tensor, approximate: str = "none"): | ||
| 76 | + """ | ||
| 77 | + y = -sqrt(8/pi) * (x + 0.044715 * x^3) | ||
| 78 | + out = x / (1 + exp(y)) | ||
| 79 | + """ | ||
| 80 | + orig_dtype = a.dtype | ||
| 81 | + if orig_dtype != torch.float32: | ||
| 82 | + a = a.to(torch.float32) | ||
| 83 | + | ||
| 84 | + M_SQRT2 = math.sqrt(2) | ||
| 85 | + M_2_SQRTPI = 2.0 / math.sqrt(math.pi) | ||
| 86 | + kBeta = M_SQRT2 * M_2_SQRTPI | ||
| 87 | + kKappa = 0.044715 | ||
| 88 | + | ||
| 89 | + a_cube = a * a * a | ||
| 90 | + inner = a + kKappa * a_cube | ||
| 91 | + y = -kBeta * inner | ||
| 92 | + out = a / (1.0 + torch.exp(y)) | ||
| 93 | + | ||
| 94 | + if orig_dtype != torch.float32: | ||
| 95 | + out = out.to(orig_dtype) | ||
| 96 | + return out | ||
| 97 | + | ||
| 98 | + | ||
| 99 | +def gelu_backward(grad, self, approximate: str = "none"): | ||
| 100 | + orig_dtype = grad.dtype | ||
| 101 | + if orig_dtype != torch.float32: | ||
| 102 | + grad = grad.to(torch.float32) | ||
| 103 | + self = self.to(torch.float32) | ||
| 104 | + M_SQRT2 = math.sqrt(2) | ||
| 105 | + M_2_SQRTPI = 2.0 / math.sqrt(math.pi) | ||
| 106 | + kBeta = M_SQRT2 * M_2_SQRTPI * 0.5 | ||
| 107 | + kKappa = 0.044715 | ||
| 108 | + x_sq = self * self | ||
| 109 | + x_cube = x_sq * self | ||
| 110 | + inner = kBeta * (self + kKappa * x_cube) | ||
| 111 | + tanh_inner = torch.tanh(inner) | ||
| 112 | + | ||
| 113 | + left = 0.5 * self | ||
| 114 | + right = 1.0 + tanh_inner | ||
| 115 | + | ||
| 116 | + left_derivative = 0.5 * right | ||
| 117 | + | ||
| 118 | + tanh_derivative = (tanh_inner * tanh_inner) * -1.0 + 1.0 | ||
| 119 | + inner_derivative = kBeta * (1.0 + 3.0 * kKappa * x_sq) | ||
| 120 | + right_derivative = left * tanh_derivative * inner_derivative | ||
| 121 | + out = grad * (left_derivative + right_derivative) | ||
| 122 | + | ||
| 123 | + if orig_dtype != torch.float32: | ||
| 124 | + out = out.to(orig_dtype) | ||
| 125 | + return out | ||
| 126 | + | ||
| 127 | + | ||
| 128 | +def sigmoid(a: torch.Tensor) -> torch.Tensor: | ||
| 129 | + orig_dtype = a.dtype | ||
| 130 | + if orig_dtype != torch.float32: | ||
| 131 | + a = a.to(torch.float32) | ||
| 132 | + out = 1 / (1.0 + torch.exp(torch.neg(a))) | ||
| 133 | + if orig_dtype != torch.float32: | ||
| 134 | + out = out.to(orig_dtype) | ||
| 135 | + return out | ||
| 136 | + | ||
| 137 | + | ||
| 138 | +_dvm_inductor_decomp_patched = False | ||
| 139 | + | ||
| 140 | + | ||
| 141 | +def _register_inductor_decomposition_safe(overloads, fn): | ||
| 142 | + """Register a custom Inductor decomposition; ignore duplicate registration.""" | ||
| 143 | + try: | ||
| 144 | + inductor_decomp.register_decomposition(overloads)(fn) | ||
| 145 | + except (RuntimeError, ValueError) as e: | ||
| 146 | + msg = str(e).lower() | ||
| 147 | + if any( | ||
| 148 | + s in msg | ||
| 149 | + for s in ( | ||
| 150 | + "duplicate", | ||
| 151 | + "already", | ||
| 152 | + "exists", | ||
| 153 | + "re-register", | ||
| 154 | + "re_register", | ||
| 155 | + ) | ||
| 156 | + ): | ||
| 157 | + return | ||
| 158 | + raise | ||
| 159 | + | ||
| 160 | + | ||
| 161 | +def patch_decomp(): | ||
| 162 | + """Patch Inductor decomposition for DVM paths (idempotent).""" | ||
| 163 | + global _dvm_inductor_decomp_patched | ||
| 164 | + if _dvm_inductor_decomp_patched: | ||
| 165 | + return | ||
| 166 | + remove_decompositions(inductor_decomp.decompositions, decomps_to_exclude_npu) | ||
| 167 | + _register_inductor_decomposition_safe([aten.sigmoid.default], sigmoid) | ||
| 168 | + _register_inductor_decomposition_safe([aten.gelu_backward.default], gelu_backward) | ||
| 169 | + _register_inductor_decomposition_safe([aten.gelu.default], gelu) | ||
| 170 | + _register_inductor_decomposition_safe([aten.tanh.default], tanh) | ||
| 171 | + _dvm_inductor_decomp_patched = True | ||
| @@ -0,0 +1,251 @@ | |||
| 1 | +from dataclasses import dataclass | ||
| 2 | + | ||
| 3 | +import torch | ||
| 4 | +from torch.fx import GraphModule, Node | ||
| 5 | +from torch._subclasses import FakeTensor | ||
| 6 | +from torch._prims_common import elementwise_dtypes, ELEMENTWISE_TYPE_PROMOTION_KIND | ||
| 7 | +from .op_emitter import _is_last2_transpose_tensor | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +aten = torch.ops.aten | ||
| 11 | +prims = torch.ops.prims | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +def need_fallback_gm(gm: torch.fx.GraphModule) -> bool: | ||
| 15 | + for node in gm.graph.nodes: | ||
| 16 | + if node.op != "call_function": | ||
| 17 | + continue | ||
| 18 | + if node.target not in ( | ||
| 19 | + aten.reshape.default, | ||
| 20 | + aten.expand.default, | ||
| 21 | + ): | ||
| 22 | + return False | ||
| 23 | + return True | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +def annotate_mm_transpose_flags(gm: torch.fx.GraphModule): | ||
| 27 | + flag = False | ||
| 28 | + for node in gm.graph.nodes: | ||
| 29 | + if node.op != "call_function": | ||
| 30 | + continue | ||
| 31 | + if node.target in [aten.mm.default, aten.bmm.default]: | ||
| 32 | + lhs = node.args[0] | ||
| 33 | + rhs = node.args[1] | ||
| 34 | + flag = True | ||
| 35 | + elif node.target is aten.addmm.default: | ||
| 36 | + add = node.args[0] | ||
| 37 | + lhs = node.args[1] | ||
| 38 | + rhs = node.args[2] | ||
| 39 | + if ( | ||
| 40 | + add.meta["val"].dim() == 1 | ||
| 41 | + and node.kwargs.get("beta", 1) == 1 | ||
| 42 | + and node.kwargs.get("alpha", 1) == 1 | ||
| 43 | + ): | ||
| 44 | + node.meta["use_bias"] = True | ||
| 45 | + flag = True | ||
| 46 | + else: | ||
| 47 | + continue | ||
| 48 | + node.meta["trans_a"] = False | ||
| 49 | + node.meta["trans_b"] = False | ||
| 50 | + if lhs.op == "placeholder" and _is_last2_transpose_tensor(lhs.meta["val"]): | ||
| 51 | + node.meta["trans_a"] = True | ||
| 52 | + lhs.meta["trans"] = True | ||
| 53 | + if rhs.op == "placeholder" and _is_last2_transpose_tensor(rhs.meta["val"]): | ||
| 54 | + node.meta["trans_b"] = True | ||
| 55 | + rhs.meta["trans"] = True | ||
| 56 | + | ||
| 57 | + return flag | ||
| 58 | + | ||
| 59 | + | ||
| 60 | +def make_cast_node(g, src: Node, target_dtype: torch.dtype) -> Node: | ||
| 61 | + cast = g.call_function( | ||
| 62 | + prims.convert_element_type.default, | ||
| 63 | + args=(src, target_dtype), | ||
| 64 | + ) | ||
| 65 | + | ||
| 66 | + cast.meta["val"] = src.meta["val"].to(dtype=target_dtype) | ||
| 67 | + return cast | ||
| 68 | + | ||
| 69 | + | ||
| 70 | +def decompose_k1_matmul_to_mul(gm: GraphModule) -> GraphModule: | ||
| 71 | + g = gm.graph | ||
| 72 | + changed = False | ||
| 73 | + | ||
| 74 | + for node in list(g.nodes): | ||
| 75 | + if node.op != "call_function": | ||
| 76 | + continue | ||
| 77 | + if node.target not in (aten.mm.default, aten.bmm.default): | ||
| 78 | + continue | ||
| 79 | + | ||
| 80 | + lhs, rhs = node.args[:2] | ||
| 81 | + | ||
| 82 | + if not isinstance(lhs, Node) or not isinstance(rhs, Node): | ||
| 83 | + continue | ||
| 84 | + | ||
| 85 | + lhs_val = lhs.meta.get("val", None) | ||
| 86 | + rhs_val = rhs.meta.get("val", None) | ||
| 87 | + if not isinstance(lhs_val, FakeTensor) or not isinstance(rhs_val, FakeTensor): | ||
| 88 | + continue | ||
| 89 | + | ||
| 90 | + if _is_last2_transpose_tensor(lhs_val): | ||
| 91 | + continue | ||
| 92 | + if _is_last2_transpose_tensor(rhs_val): | ||
| 93 | + continue | ||
| 94 | + | ||
| 95 | + lhs_k = lhs_val.shape[-1] | ||
| 96 | + rhs_k = rhs_val.shape[-2] | ||
| 97 | + | ||
| 98 | + if isinstance(lhs_k, torch.SymInt) or isinstance(rhs_k, torch.SymInt): | ||
| 99 | + continue | ||
| 100 | + if lhs_k != 1 or rhs_k != 1: | ||
| 101 | + continue | ||
| 102 | + | ||
| 103 | + with g.inserting_before(node): | ||
| 104 | + mul_node = g.call_function(aten.mul.Tensor, args=(lhs, rhs)) | ||
| 105 | + mul_node.meta["val"] = node.meta["val"] | ||
| 106 | + | ||
| 107 | + node.replace_all_uses_with(mul_node) | ||
| 108 | + g.erase_node(node) | ||
| 109 | + changed = True | ||
| 110 | + | ||
| 111 | + if changed: | ||
| 112 | + g.lint() | ||
| 113 | + gm.recompile() | ||
| 114 | + return gm | ||
| 115 | + | ||
| 116 | + | ||
| 117 | +def insert_sum_fp32_prepost_cast_prims(gm: GraphModule): | ||
| 118 | + g = gm.graph | ||
| 119 | + for node in g.nodes: | ||
| 120 | + if node.op != "call_function": | ||
| 121 | + continue | ||
| 122 | + if node.target not in [aten.sum.default, aten.sum.dim_IntList]: | ||
| 123 | + continue | ||
| 124 | + | ||
| 125 | + out_val = node.meta.get("val", None) | ||
| 126 | + if not isinstance(out_val, FakeTensor): | ||
| 127 | + continue | ||
| 128 | + orig_out_dtype = out_val.dtype | ||
| 129 | + | ||
| 130 | + if not node.args: | ||
| 131 | + continue | ||
| 132 | + x = node.args[0] | ||
| 133 | + if not isinstance(x, Node): | ||
| 134 | + continue | ||
| 135 | + | ||
| 136 | + in_val = x.meta.get("val", None) | ||
| 137 | + if not isinstance(in_val, FakeTensor): | ||
| 138 | + continue | ||
| 139 | + in_dtype = in_val.dtype | ||
| 140 | + | ||
| 141 | + if in_dtype == torch.float32: | ||
| 142 | + continue | ||
| 143 | + | ||
| 144 | + with g.inserting_before(node): | ||
| 145 | + x_fp32 = make_cast_node(g, x, torch.float32) | ||
| 146 | + | ||
| 147 | + new_args = list(node.args) | ||
| 148 | + new_args[0] = x_fp32 | ||
| 149 | + node.args = tuple(new_args) | ||
| 150 | + | ||
| 151 | + if orig_out_dtype != torch.float32: | ||
| 152 | + with g.inserting_after(node): | ||
| 153 | + y = make_cast_node(g, node, orig_out_dtype) | ||
| 154 | + node.replace_all_uses_with(y) | ||
| 155 | + y.args = (node, orig_out_dtype) | ||
| 156 | + | ||
| 157 | + g.lint() | ||
| 158 | + gm.recompile() | ||
| 159 | + return gm | ||
| 160 | + | ||
| 161 | + | ||
| 162 | + | ||
| 163 | +class PromoteRule: | ||
| 164 | + pos: tuple[int, ...] | ||
| 165 | + kind: object = ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT | ||
| 166 | + | ||
| 167 | + | ||
| 168 | +PROMOTE_TYPE_OP = { | ||
| 169 | + # ========= Elementwise ========= | ||
| 170 | + aten.add.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 171 | + aten.sub.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 172 | + aten.mul.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 173 | + aten.div.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 174 | + aten.pow.Tensor_Tensor: PromoteRule( | ||
| 175 | + (0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT | ||
| 176 | + ), | ||
| 177 | + aten.lt.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.ALWAYS_BOOL), | ||
| 178 | + aten.le.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.ALWAYS_BOOL), | ||
| 179 | + aten.gt.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.ALWAYS_BOOL), | ||
| 180 | + aten.ge.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.ALWAYS_BOOL), | ||
| 181 | + aten.eq.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.ALWAYS_BOOL), | ||
| 182 | + aten.ne.Tensor: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.ALWAYS_BOOL), | ||
| 183 | + aten.maximum.default: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 184 | + aten.minimum.default: PromoteRule((0, 1), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 185 | + # ========= Where ========= | ||
| 186 | + # where(cond, x, y) → promote x/y | ||
| 187 | + # aten.where.default: PromoteRule((1, 2), ELEMENTWISE_TYPE_PROMOTION_KIND.DEFAULT), | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | + | ||
| 191 | +def insert_promote_cast_by_pos_prims(gm: GraphModule) -> GraphModule: | ||
| 192 | + g = gm.graph | ||
| 193 | + | ||
| 194 | + for node in g.nodes: | ||
| 195 | + if node.op != "call_function": | ||
| 196 | + continue | ||
| 197 | + | ||
| 198 | + rule = PROMOTE_TYPE_OP.get(node.target, None) | ||
| 199 | + if rule is None: | ||
| 200 | + continue | ||
| 201 | + | ||
| 202 | + arg_vals = [] | ||
| 203 | + arg_nodes = {} | ||
| 204 | + | ||
| 205 | + for idx in rule.pos: | ||
| 206 | + if idx >= len(node.args): | ||
| 207 | + continue | ||
| 208 | + arg = node.args[idx] | ||
| 209 | + if not isinstance(arg, Node): | ||
| 210 | + continue | ||
| 211 | + val = arg.meta.get("val", None) | ||
| 212 | + if not isinstance(val, FakeTensor): | ||
| 213 | + continue | ||
| 214 | + arg_vals.append(val) | ||
| 215 | + arg_nodes[idx] = arg | ||
| 216 | + | ||
| 217 | + if len(arg_vals) <= 1: | ||
| 218 | + continue | ||
| 219 | + | ||
| 220 | + dtypes = [v.dtype for v in arg_vals] | ||
| 221 | + if all(dt == dtypes[0] for dt in dtypes[1:]): | ||
| 222 | + continue | ||
| 223 | + | ||
| 224 | + compute_dtype, _ = elementwise_dtypes( | ||
| 225 | + *arg_vals, | ||
| 226 | + type_promotion_kind=rule.kind, | ||
| 227 | + ) | ||
| 228 | + | ||
| 229 | + new_args = list(node.args) | ||
| 230 | + for idx, arg in arg_nodes.items(): | ||
| 231 | + in_val = arg.meta.get("val", None) | ||
| 232 | + if in_val.dtype == compute_dtype: | ||
| 233 | + continue | ||
| 234 | + with g.inserting_before(node): | ||
| 235 | + cast = make_cast_node(g, arg, compute_dtype) | ||
| 236 | + new_args[idx] = cast | ||
| 237 | + | ||
| 238 | + node.args = tuple(new_args) | ||
| 239 | + | ||
| 240 | + g.lint() | ||
| 241 | + gm.recompile() | ||
| 242 | + return gm | ||
| 243 | + | ||
| 244 | + | ||
| 245 | +def expand_to_reshape(gm: GraphModule) -> GraphModule: | ||
| 246 | + for node in gm.graph.find_nodes(op="call_function", target=aten.expand.default): | ||
| 247 | + x = node.args[0] | ||
| 248 | + in_val = x.meta.get("val") | ||
| 249 | + out_val = node.meta.get("val") | ||
| 250 | + if tuple(in_val.shape) == tuple(out_val.shape): | ||
| 251 | + node.target = aten.reshape.default | ||
| @@ -0,0 +1,145 @@ | |||
| 1 | +import os | ||
| 2 | +import hashlib | ||
| 3 | +import torch | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +DEFAULT_OUTPUT_DIR = "./dvm_fx_regression_cases" | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +def generate_dvm_fx_case( | ||
| 10 | + gm: torch.fx.GraphModule, | ||
| 11 | + output_dir: str = DEFAULT_OUTPUT_DIR, | ||
| 12 | + fusion_type: str = "graph", | ||
| 13 | +): | ||
| 14 | + if fusion_type not in ("graph", "mlir"): | ||
| 15 | + raise ValueError(f"unsupported fusion_type: {fusion_type}") | ||
| 16 | + | ||
| 17 | + def _indent(code: str, spaces: int) -> str: | ||
| 18 | + pad = " " * spaces | ||
| 19 | + return "\n".join( | ||
| 20 | + pad + line if line.strip() else line | ||
| 21 | + for line in code.split("\n") | ||
| 22 | + ) | ||
| 23 | + | ||
| 24 | + os.makedirs(output_dir, exist_ok=True) | ||
| 25 | + | ||
| 26 | + readable = gm.print_readable(print_output=False) | ||
| 27 | + | ||
| 28 | + sig = [] | ||
| 29 | + inputs = [n for n in gm.graph.nodes if n.op == "placeholder"] | ||
| 30 | + for i, n in enumerate(inputs): | ||
| 31 | + v = n.meta["val"] | ||
| 32 | + sig.append(f"arg{i}:{tuple(v.shape)},{tuple(v.stride())},{v.dtype}") | ||
| 33 | + | ||
| 34 | + h = hashlib.sha256( | ||
| 35 | + (fusion_type + "\n" + readable + "\n" + "\n".join(sig)).encode("utf-8") | ||
| 36 | + ).hexdigest()[:16] | ||
| 37 | + case_name = f"test_{fusion_type}_{h}" | ||
| 38 | + | ||
| 39 | + file_path = os.path.join(output_dir, f"{case_name}.py") | ||
| 40 | + if os.path.exists(file_path): | ||
| 41 | + print(f"[skip] {file_path}") | ||
| 42 | + return None | ||
| 43 | + class_name = "TestModel" | ||
| 44 | + | ||
| 45 | + input_lines = [] | ||
| 46 | + for i, n in enumerate(inputs): | ||
| 47 | + v = n.meta["val"] | ||
| 48 | + fill = "random_()" if v.dtype == torch.bool else "uniform_(0, 1)" | ||
| 49 | + input_lines.append( | ||
| 50 | + f"arg{i} = torch.empty_strided(" | ||
| 51 | + f"torch.Size({tuple(v.shape)}), " | ||
| 52 | + f"{tuple(v.stride())}, " | ||
| 53 | + f"dtype={v.dtype}, device='npu').{fill}" | ||
| 54 | + ) | ||
| 55 | + | ||
| 56 | + input_code = "\n ".join(input_lines) | ||
| 57 | + fwd_args = ", ".join(f"arg{i}" for i in range(len(inputs))) | ||
| 58 | + | ||
| 59 | + if fusion_type == "graph": | ||
| 60 | + fusion_env = "" | ||
| 61 | + fusion_imports = ( | ||
| 62 | + "from torch_npu._inductor.dvm.graph_fusion " | ||
| 63 | + "import DvmGraphFusionPatch" | ||
| 64 | + ) | ||
| 65 | + compile_lines = [ | ||
| 66 | + "with DvmGraphFusionPatch():", | ||
| 67 | + " compiled = torch.compile(model, backend=\"inductor\", dynamic=False)", | ||
| 68 | + f" out = compiled({fwd_args})", | ||
| 69 | + " deterministic_state = torch.are_deterministic_algorithms_enabled()", | ||
| 70 | + " deterministic_warn_only = torch.is_deterministic_algorithms_warn_only_enabled()", | ||
| 71 | + " try:", | ||
| 72 | + " torch.use_deterministic_algorithms(True)", | ||
| 73 | + " deterministic_compiled = torch.compile(model, backend=\"inductor\", dynamic=False)", | ||
| 74 | + f" deterministic_out = deterministic_compiled({fwd_args})", | ||
| 75 | + " finally:", | ||
| 76 | + " torch.use_deterministic_algorithms(deterministic_state, warn_only=deterministic_warn_only)", | ||
| 77 | + ] | ||
| 78 | + else: | ||
| 79 | + fusion_env = 'os.environ["TORCHINDUCTOR_NPU_BACKEND"] = "dvm"' | ||
| 80 | + fusion_imports = "from torch_npu._inductor.dvm import mlir_fusion" | ||
| 81 | + compile_lines = [ | ||
| 82 | + "compiled = torch.compile(model, backend=\"inductor\", dynamic=False)", | ||
| 83 | + f"out = compiled({fwd_args})", | ||
| 84 | + "deterministic_state = torch.are_deterministic_algorithms_enabled()", | ||
| 85 | + "deterministic_warn_only = torch.is_deterministic_algorithms_warn_only_enabled()", | ||
| 86 | + "try:", | ||
| 87 | + " torch.use_deterministic_algorithms(True)", | ||
| 88 | + " deterministic_compiled = torch.compile(model, backend=\"inductor\", dynamic=False)", | ||
| 89 | + f" deterministic_out = deterministic_compiled({fwd_args})", | ||
| 90 | + "finally:", | ||
| 91 | + " torch.use_deterministic_algorithms(deterministic_state, warn_only=deterministic_warn_only)", | ||
| 92 | + ] | ||
| 93 | + compile_code = "\n ".join(compile_lines) | ||
| 94 | + env_lines = fusion_env | ||
| 95 | + | ||
| 96 | + test_code = f"""import os | ||
| 97 | + | ||
| 98 | +os.environ["INDUCTOR_DVM_DUMP_FX_TEST"] = "0" | ||
| 99 | +{env_lines} | ||
| 100 | + | ||
| 101 | +import torch | ||
| 102 | +import torch_npu | ||
| 103 | +from torch import device, tensor | ||
| 104 | +from math import inf, nan | ||
| 105 | +from torch.utils._pytree import tree_flatten | ||
| 106 | + | ||
| 107 | + | ||
| 108 | +class {class_name}(torch.nn.Module): | ||
| 109 | + def __init__(self): | ||
| 110 | + super().__init__() | ||
| 111 | +{_indent(gm.code, 4)} | ||
| 112 | + | ||
| 113 | + | ||
| 114 | +def _assert_close(ref, out, atol=2e-3, rtol=2e-3): | ||
| 115 | + rf, rs = tree_flatten(ref) | ||
| 116 | + of, os = tree_flatten(out) | ||
| 117 | + assert rs == os, f"pytree mismatch\\nref={{rs}}\\nout={{os}}" | ||
| 118 | + for r, o in zip(rf, of): | ||
| 119 | + torch.testing.assert_close(r, o, atol=atol, rtol=rtol, equal_nan=True) | ||
| 120 | + | ||
| 121 | + | ||
| 122 | +def test_case(): | ||
| 123 | + {input_code} | ||
| 124 | + | ||
| 125 | + model = {class_name}().npu() | ||
| 126 | + ref = model({fwd_args}) | ||
| 127 | + | ||
| 128 | + {fusion_imports} | ||
| 129 | + | ||
| 130 | + {compile_code} | ||
| 131 | + | ||
| 132 | + _assert_close(ref, out) | ||
| 133 | + _assert_close(ref, deterministic_out) | ||
| 134 | + | ||
| 135 | + | ||
| 136 | +if __name__ == "__main__": | ||
| 137 | + test_case() | ||
| 138 | + print("PASS") | ||
| 139 | +""" | ||
| 140 | + | ||
| 141 | + with open(file_path, "w", encoding="utf-8") as f: | ||
| 142 | + f.write(test_code) | ||
| 143 | + | ||
| 144 | + print(f"[ok] generated: {file_path}") | ||
| 145 | + return file_path | ||
| @@ -0,0 +1,165 @@ | |||
| 1 | +import torch | ||
| 2 | +import torch.utils._pytree as pytree | ||
| 3 | +from torch._inductor.utils import IndentedBuffer | ||
| 4 | +from torch.fx.node import Argument, Target | ||
| 5 | + | ||
| 6 | +from .fx_pass import annotate_mm_transpose_flags | ||
| 7 | +from .op_emitter import DVM_OP_REGISTRY, load, store | ||
| 8 | +from .util import codegen_maybe_view_load | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +aten = torch.ops.aten | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +def is_fx_dynamic(graph): | ||
| 15 | + for node in graph.graph.nodes: | ||
| 16 | + if node.op == "placeholder" or node.op == "call_function": | ||
| 17 | + val = node.meta.get("val") | ||
| 18 | + if val is None: | ||
| 19 | + continue | ||
| 20 | + if isinstance(val, torch.Tensor): | ||
| 21 | + if any(isinstance(dim, torch.SymInt) for dim in val.shape): | ||
| 22 | + return True | ||
| 23 | + elif isinstance(val, (torch.SymInt, torch.SymFloat)): | ||
| 24 | + return True | ||
| 25 | + return False | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +class DvmCodegenInterpreter(torch.fx.Interpreter): | ||
| 29 | + KERNEL_NAME_PLACEHOLDER = "__DVM_KERNEL_NAME__" | ||
| 30 | + | ||
| 31 | + def __init__( | ||
| 32 | + self, | ||
| 33 | + gm: torch.fx.GraphModule, | ||
| 34 | + ktype: str, | ||
| 35 | + view_fusion_level=1, | ||
| 36 | + is_dynamic: bool | None = None, | ||
| 37 | + ): | ||
| 38 | + super().__init__(gm) | ||
| 39 | + self.gm = gm | ||
| 40 | + self.ktype = ktype | ||
| 41 | + self.is_mix_kernel = annotate_mm_transpose_flags(gm) | ||
| 42 | + if is_dynamic is None: | ||
| 43 | + self.is_dynamic = is_fx_dynamic(gm) | ||
| 44 | + else: | ||
| 45 | + if not isinstance(is_dynamic, bool): | ||
| 46 | + raise TypeError("is_dynamic must be bool when provided") | ||
| 47 | + self.is_dynamic = is_dynamic | ||
| 48 | + self.current_node = None | ||
| 49 | + self.cont_flag_input = [] | ||
| 50 | + self.need_trans_input = [] | ||
| 51 | + self.view_fusion_level = view_fusion_level | ||
| 52 | + self.code = IndentedBuffer() | ||
| 53 | + | ||
| 54 | + self.spec_nodes = set() | ||
| 55 | + if self.ktype == "vector" and self.need_spec(): | ||
| 56 | + self.ktype = "spec" | ||
| 57 | + self.code.splice(f'\n"""\n{self.gm.print_readable(print_output=False)}\n"""') | ||
| 58 | + decorator = ( | ||
| 59 | + f"{chr(64)}dvm.kernel(ktype={self.ktype!r}, dyn_shape={self.is_dynamic})" | ||
| 60 | + ) | ||
| 61 | + self.code.splice(decorator) | ||
| 62 | + self.code.splice(f"def {self.KERNEL_NAME_PLACEHOLDER}(k):") | ||
| 63 | + self.code.do_indent() | ||
| 64 | + | ||
| 65 | + def need_spec(self) -> bool: | ||
| 66 | + self.spec_nodes.clear() | ||
| 67 | + for node in self.gm.graph.nodes: | ||
| 68 | + if node.op != "call_function": | ||
| 69 | + continue | ||
| 70 | + for input_node in node.all_input_nodes: | ||
| 71 | + if input_node.op == "call_function" and input_node.target in [ | ||
| 72 | + aten.sum.default, | ||
| 73 | + aten.sum.dim_IntList, | ||
| 74 | + aten.amax.default, | ||
| 75 | + aten.amin.default, | ||
| 76 | + ]: | ||
| 77 | + self.spec_nodes.add(input_node) | ||
| 78 | + return len(self.spec_nodes) > 0 | ||
| 79 | + | ||
| 80 | + def run_node(self, n: torch.fx.Node) -> Argument: | ||
| 81 | + self.current_node = n | ||
| 82 | + expr = super().run_node(n) | ||
| 83 | + if n.op == "output": | ||
| 84 | + for _expr in pytree.tree_leaves(expr): | ||
| 85 | + self.code.splice(f"{_expr}") | ||
| 86 | + else: | ||
| 87 | + self.code.splice(f"{n} = {expr}") | ||
| 88 | + if n in self.spec_nodes: | ||
| 89 | + self.code.splice("k.spec_next()") | ||
| 90 | + return f"{n}" | ||
| 91 | + | ||
| 92 | + def placeholder( | ||
| 93 | + self, target: "Target", args: tuple[Argument], kwargs: dict[str, Argument] | ||
| 94 | + ) -> Argument: | ||
| 95 | + meta = self.current_node.meta | ||
| 96 | + val = meta["val"] | ||
| 97 | + if isinstance(val, torch.SymInt): | ||
| 98 | + self.cont_flag_input.append(True) | ||
| 99 | + return "k.scalar(dvm.int64)" | ||
| 100 | + if isinstance(val, torch.SymFloat): | ||
| 101 | + self.cont_flag_input.append(True) | ||
| 102 | + return "k.scalar(dvm.float32)" | ||
| 103 | + | ||
| 104 | + shape, stride, dtype = val.shape, val.stride(), val.dtype | ||
| 105 | + is_symbolic = any( | ||
| 106 | + isinstance(s, torch.SymInt) and s.node.is_symbolic() for s in shape | ||
| 107 | + ) | ||
| 108 | + is_contiguous = val.is_contiguous() | ||
| 109 | + | ||
| 110 | + if self.is_mix_kernel: | ||
| 111 | + trans = meta.get("trans", False) | ||
| 112 | + self.need_trans_input.append(trans) | ||
| 113 | + self.cont_flag_input.append(trans or is_contiguous) | ||
| 114 | + if trans: | ||
| 115 | + shape = val.mT.shape | ||
| 116 | + shape = [-1 if isinstance(s, torch.SymInt) else s for s in shape] | ||
| 117 | + return load(shape, dtype) | ||
| 118 | + | ||
| 119 | + shape = [-1 if isinstance(s, torch.SymInt) else s for s in shape] | ||
| 120 | + stride = [-1 if isinstance(s, torch.SymInt) else s for s in stride] | ||
| 121 | + if is_contiguous: | ||
| 122 | + expr, skip_cont = load(shape, dtype), True | ||
| 123 | + else: | ||
| 124 | + expr, skip_cont = codegen_maybe_view_load( | ||
| 125 | + shape, | ||
| 126 | + stride, | ||
| 127 | + dtype, | ||
| 128 | + view_fusion_level=self.view_fusion_level, | ||
| 129 | + is_symbolic=is_symbolic, | ||
| 130 | + ) | ||
| 131 | + self.cont_flag_input.append(skip_cont) | ||
| 132 | + return expr | ||
| 133 | + | ||
| 134 | + def call_function( | ||
| 135 | + self, target: "Target", args: tuple[Argument, ...], kwargs: dict[str, Argument] | ||
| 136 | + ) -> Argument: | ||
| 137 | + if target not in DVM_OP_REGISTRY: | ||
| 138 | + raise NotImplementedError(f"{target} not implemented in DVM") | ||
| 139 | + func, _ = DVM_OP_REGISTRY.get(target) | ||
| 140 | + meta = self.current_node.meta | ||
| 141 | + | ||
| 142 | + if target in (aten.mm.default, aten.bmm.default): | ||
| 143 | + args = (*args, meta.get("trans_a", False), meta.get("trans_b", False)) | ||
| 144 | + | ||
| 145 | + elif target is aten.addmm.default: | ||
| 146 | + args = ( | ||
| 147 | + *args, | ||
| 148 | + meta.get("trans_a", False), | ||
| 149 | + meta.get("trans_b", False), | ||
| 150 | + meta.get("use_bias", False), | ||
| 151 | + ) | ||
| 152 | + | ||
| 153 | + return func(*args, **kwargs) | ||
| 154 | + | ||
| 155 | + def output( | ||
| 156 | + self, target: "Target", args: tuple[Argument, ...], kwargs: dict[str, Argument] | ||
| 157 | + ) -> Argument: | ||
| 158 | + outs = super().output(target, args, kwargs) | ||
| 159 | + | ||
| 160 | + def codegen(out, node): | ||
| 161 | + if isinstance(node, torch.fx.Node): | ||
| 162 | + return store(out, node.meta["val"].dtype) | ||
| 163 | + return "" | ||
| 164 | + | ||
| 165 | + return pytree.tree_map(codegen, outs, self.current_node.args[0]) | ||
| @@ -0,0 +1,437 @@ | |||
| 1 | +import os | ||
| 2 | +from collections import defaultdict | ||
| 3 | +from types import SimpleNamespace | ||
| 4 | + | ||
| 5 | +import torch | ||
| 6 | +from torch.fx import Graph, GraphModule, Node | ||
| 7 | +from torch.library import Library | ||
| 8 | +from torch.utils._pytree import tree_map | ||
| 9 | +from torch._inductor import config as inductor_config | ||
| 10 | +from torch._inductor.codegen.wrapper import PythonWrapperCodegen | ||
| 11 | +from torch._inductor.virtualized import V | ||
| 12 | +from torch._subclasses import FakeTensor | ||
| 13 | + | ||
| 14 | +from torch.fx.passes.infra.partitioner import CapabilityBasedPartitioner | ||
| 15 | +from torch.fx.passes.operator_support import OperatorSupportBase | ||
| 16 | +from torch.fx.passes.tools_common import legalize_graph | ||
| 17 | +from torch.fx.passes.utils.fuser_utils import ( | ||
| 18 | + topo_sort, | ||
| 19 | + fuse_as_graphmodule, | ||
| 20 | + erase_nodes, | ||
| 21 | +) | ||
| 22 | + | ||
| 23 | +from .graph_build import DvmCodegenInterpreter, is_fx_dynamic | ||
| 24 | +from .util import patch_gm_placeholder_strides_from_codegen_args | ||
| 25 | +from .fx_test import generate_dvm_fx_case | ||
| 26 | +from .op_emitter import DVM_OP_REGISTRY | ||
| 27 | +from .fx_pass import ( | ||
| 28 | + decompose_k1_matmul_to_mul, | ||
| 29 | + insert_promote_cast_by_pos_prims, | ||
| 30 | + insert_sum_fp32_prepost_cast_prims, | ||
| 31 | + expand_to_reshape, | ||
| 32 | + need_fallback_gm, | ||
| 33 | +) | ||
| 34 | + | ||
| 35 | +dump_fx_test = os.environ.get("INDUCTOR_DVM_DUMP_FX_TEST", "0") == "1" | ||
| 36 | + | ||
| 37 | +view_fusion_level = int(os.environ.get("INDUCTOR_DVM_VIEW_FUSION_LEVEL", "1")) | ||
| 38 | + | ||
| 39 | +aten = torch.ops.aten | ||
| 40 | +prims = torch.ops.prims | ||
| 41 | + | ||
| 42 | +GRAPH_FUSION_SUPPORT_OP = [ | ||
| 43 | + aten.add.Tensor, | ||
| 44 | + aten.add.Scalar, | ||
| 45 | + aten.sub.Tensor, | ||
| 46 | + aten.sub.Scalar, | ||
| 47 | + aten.mul.Tensor, | ||
| 48 | + aten.mul.Scalar, | ||
| 49 | + aten.div.Tensor, | ||
| 50 | + aten.div.Scalar, | ||
| 51 | + aten.pow.Tensor_Tensor, | ||
| 52 | + aten.pow.Tensor_Scalar, | ||
| 53 | + aten.pow.Scalar, | ||
| 54 | + aten.lt.Tensor, | ||
| 55 | + aten.lt.Scalar, | ||
| 56 | + aten.le.Tensor, | ||
| 57 | + aten.le.Scalar, | ||
| 58 | + aten.gt.Tensor, | ||
| 59 | + aten.gt.Scalar, | ||
| 60 | + aten.ge.Tensor, | ||
| 61 | + aten.ge.Scalar, | ||
| 62 | + aten.eq.Tensor, | ||
| 63 | + aten.eq.Scalar, | ||
| 64 | + aten.ne.Tensor, | ||
| 65 | + aten.ne.Scalar, | ||
| 66 | + aten.maximum.default, | ||
| 67 | + aten.minimum.default, | ||
| 68 | + aten.sqrt.default, | ||
| 69 | + aten.rsqrt.default, | ||
| 70 | + aten.abs.default, | ||
| 71 | + aten.log.default, | ||
| 72 | + aten.exp.default, | ||
| 73 | + aten.reciprocal.default, | ||
| 74 | + aten.isfinite.default, | ||
| 75 | + prims.convert_element_type.default, | ||
| 76 | + torch.ops.npu.npu_dtype_cast.default, | ||
| 77 | + torch.ops.npu.npu_dtype_cast_backward.default, | ||
| 78 | + torch.ops.npu._npu_dtype_cast.default, | ||
| 79 | + torch.ops.npu._npu_dtype_cast_backward.default, | ||
| 80 | + aten.sum.dim_IntList, | ||
| 81 | + aten.sum.default, | ||
| 82 | + aten.neg.default, | ||
| 83 | + aten.relu.default, | ||
| 84 | + aten.mm.default, | ||
| 85 | + aten.bmm.default, | ||
| 86 | + aten.addmm.default, | ||
| 87 | + aten.where.default, | ||
| 88 | + aten.where.self, | ||
| 89 | + # aten.expand.default, | ||
| 90 | + aten.full.default, | ||
| 91 | + # aten.reshape.default, | ||
| 92 | +] | ||
| 93 | + | ||
| 94 | +class UnionFind: | ||
| 95 | + def __init__(self) -> None: | ||
| 96 | + self.parent: dict[Node, Node] = {} | ||
| 97 | + self.rank: dict[Node, int] = {} | ||
| 98 | + | ||
| 99 | + def find(self, x: Node) -> Node: | ||
| 100 | + p = self.parent.get(x, x) | ||
| 101 | + if p != x: | ||
| 102 | + self.parent[x] = self.find(p) | ||
| 103 | + else: | ||
| 104 | + self.parent[x] = x | ||
| 105 | + return self.parent[x] | ||
| 106 | + | ||
| 107 | + def union(self, a: Node, b: Node) -> None: | ||
| 108 | + ra, rb = self.find(a), self.find(b) | ||
| 109 | + if ra == rb: | ||
| 110 | + return | ||
| 111 | + | ||
| 112 | + rka = self.rank.get(ra, 0) | ||
| 113 | + rkb = self.rank.get(rb, 0) | ||
| 114 | + if rka < rkb: | ||
| 115 | + ra, rb = rb, ra | ||
| 116 | + rka, rkb = rkb, rka | ||
| 117 | + | ||
| 118 | + self.parent[rb] = ra | ||
| 119 | + if rka == rkb: | ||
| 120 | + self.rank[ra] = rka + 1 | ||
| 121 | + | ||
| 122 | + | ||
| 123 | +class DvmOpSupport(OperatorSupportBase): | ||
| 124 | + def is_node_supported(self, submodules, node): | ||
| 125 | + if node.op == "call_function" and node.target in GRAPH_FUSION_SUPPORT_OP: | ||
| 126 | + _, rule = DVM_OP_REGISTRY.get(node.target) | ||
| 127 | + return rule(node) | ||
| 128 | + return False | ||
| 129 | + | ||
| 130 | + | ||
| 131 | +def split_partition_with_union_find( | ||
| 132 | + partition_nodes: dict[Node, None], | ||
| 133 | +) -> list[dict[Node, None]]: | ||
| 134 | + """ | ||
| 135 | + Split a partition into connected components based on data-dependency edges | ||
| 136 | + within the partition: if u is an input of v and both are in partition, | ||
| 137 | + they belong to the same component. | ||
| 138 | + """ | ||
| 139 | + nodes = list(partition_nodes.keys()) | ||
| 140 | + node_set = set(nodes) | ||
| 141 | + | ||
| 142 | + uf = UnionFind() | ||
| 143 | + for n in nodes: | ||
| 144 | + uf.find(n) | ||
| 145 | + | ||
| 146 | + for v in nodes: | ||
| 147 | + for u in v.all_input_nodes: | ||
| 148 | + if u in node_set: | ||
| 149 | + uf.union(u, v) | ||
| 150 | + | ||
| 151 | + groups: dict[Node, dict[Node, None]] = defaultdict(dict) | ||
| 152 | + for n in nodes: | ||
| 153 | + root = uf.find(n) | ||
| 154 | + groups[root][n] = None | ||
| 155 | + | ||
| 156 | + return list(groups.values()) | ||
| 157 | + | ||
| 158 | + | ||
| 159 | +class _FusedMeta: | ||
| 160 | + def __init__(self, name: str, gm: GraphModule, input_nodes: list[Node]): | ||
| 161 | + self.name = name | ||
| 162 | + self.gm = gm | ||
| 163 | + | ||
| 164 | + def codegen(self): | ||
| 165 | + """ | ||
| 166 | + Return (codegen_interpreter, python_source_string). | ||
| 167 | + """ | ||
| 168 | + if dump_fx_test: | ||
| 169 | + generate_dvm_fx_case(self.gm, fusion_type="graph") | ||
| 170 | + cg = DvmCodegenInterpreter( | ||
| 171 | + self.gm, ktype="split", view_fusion_level=view_fusion_level | ||
| 172 | + ) | ||
| 173 | + cg.run() | ||
| 174 | + code = cg.code.getvalue().replace(cg.KERNEL_NAME_PLACEHOLDER, self.name) | ||
| 175 | + return cg, code | ||
| 176 | + | ||
| 177 | + | ||
| 178 | +# fused_id -> meta | ||
| 179 | +_fused_metas: dict[int, _FusedMeta] = {} | ||
| 180 | +_fused_libs: dict[str, Library] = {} | ||
| 181 | + | ||
| 182 | + | ||
| 183 | +def _fused_run_stub(*args, **kwargs): | ||
| 184 | + raise AssertionError("This op should never run at eager runtime.") | ||
| 185 | + | ||
| 186 | + | ||
| 187 | +def _fused_run_fake(*args, **kwargs): | ||
| 188 | + """ | ||
| 189 | + Fake implementation for custom_op. The last arg is fused_id. | ||
| 190 | + We execute sub_gm.forward on meta tensors and return contiguous meta outputs. | ||
| 191 | + """ | ||
| 192 | + fused_id = int(args[-1]) | ||
| 193 | + meta = _fused_metas[fused_id] | ||
| 194 | + out = meta.gm.forward(*args[:-1]) | ||
| 195 | + return tree_map(lambda t: t if t.is_contiguous() else t.contiguous(), out) | ||
| 196 | + | ||
| 197 | + | ||
| 198 | +class GraphFusionPartitioner(CapabilityBasedPartitioner): | ||
| 199 | + fused_op_map_: dict[str, object] = {} | ||
| 200 | + fused_id_: int = 0 | ||
| 201 | + | ||
| 202 | + def _input_schema_types(self, input_nodes: list[Node]) -> list[str]: | ||
| 203 | + input_types: list[str] = [] | ||
| 204 | + for node in input_nodes: | ||
| 205 | + val = node.meta.get("val", None) | ||
| 206 | + if isinstance(val, torch.SymInt): | ||
| 207 | + input_types.append("SymInt") | ||
| 208 | + elif isinstance(val, torch.SymFloat): | ||
| 209 | + input_types.append("SymFloat") | ||
| 210 | + else: | ||
| 211 | + input_types.append("Tensor") | ||
| 212 | + return input_types | ||
| 213 | + | ||
| 214 | + def _build_schema(self, input_types: list[str], output_len: int) -> str: | ||
| 215 | + extra_inputs = "int fused_id" | ||
| 216 | + input_schema = ", ".join( | ||
| 217 | + f"{input_type} x{i}" for i, input_type in enumerate(input_types) | ||
| 218 | + ) | ||
| 219 | + input_schema = ( | ||
| 220 | + f"{input_schema}, {extra_inputs}" if input_schema else extra_inputs | ||
| 221 | + ) | ||
| 222 | + | ||
| 223 | + if output_len == 1: | ||
| 224 | + output_schema = "Tensor" | ||
| 225 | + else: | ||
| 226 | + output_schema = f'({", ".join(["Tensor"] * output_len)})' | ||
| 227 | + return f"({input_schema}) -> {output_schema}" | ||
| 228 | + | ||
| 229 | + def _get_or_create_custom_op(self, input_nodes: list[Node], output_len: int): | ||
| 230 | + input_types = self._input_schema_types(input_nodes) | ||
| 231 | + type_suffix = "_".join( | ||
| 232 | + "t" if t == "Tensor" else "si" if t == "SymInt" else "sf" | ||
| 233 | + for t in input_types | ||
| 234 | + ) | ||
| 235 | + input_len = len(input_types) | ||
| 236 | + op_def = f"{input_len}_{output_len}" | ||
| 237 | + if any(t != "Tensor" for t in input_types): | ||
| 238 | + op_def = f"{op_def}_{type_suffix}" | ||
| 239 | + custom = self.fused_op_map_.get(op_def, None) | ||
| 240 | + if custom is not None: | ||
| 241 | + return custom | ||
| 242 | + schema = self._build_schema(input_types, output_len) | ||
| 243 | + op_name = "fused_graph_" + op_def | ||
| 244 | + qualname = "dvm::" + op_name | ||
| 245 | + | ||
| 246 | + lib = Library("dvm", "FRAGMENT") | ||
| 247 | + # Use flexible_layout so shared producers can stay shared, instead of | ||
| 248 | + # each fused_graph consumer materializing its own fixed-stride copy. | ||
| 249 | + lib.define(op_name + schema, tags=[torch._C.Tag.flexible_layout]) | ||
| 250 | + lib._register_fake(op_name, _fused_run_fake, _stacklevel=1) | ||
| 251 | + lib.impl(op_name, _fused_run_stub, "CompositeExplicitAutograd") | ||
| 252 | + | ||
| 253 | + opoverload = getattr(torch.ops.dvm, op_name).default | ||
| 254 | + custom = SimpleNamespace(_opoverload=opoverload, _lib=lib, _qualname=qualname) | ||
| 255 | + _fused_libs[qualname] = lib | ||
| 256 | + self.fused_op_map_[op_def] = custom | ||
| 257 | + return custom | ||
| 258 | + | ||
| 259 | + def _should_fuse( | ||
| 260 | + self, | ||
| 261 | + sub_gm: GraphModule, | ||
| 262 | + orig_outputs: list[Node], | ||
| 263 | + orig_inputs: list[Node], | ||
| 264 | + ) -> bool: | ||
| 265 | + if len(orig_outputs) == 0: | ||
| 266 | + return False | ||
| 267 | + if need_fallback_gm(sub_gm): | ||
| 268 | + return False | ||
| 269 | + return any( | ||
| 270 | + isinstance(node.meta.get("val", None), FakeTensor) | ||
| 271 | + for node in [*orig_inputs, *orig_outputs] | ||
| 272 | + ) | ||
| 273 | + | ||
| 274 | + def partition_and_fuse(self) -> GraphModule: | ||
| 275 | + partitions = self.propose_partitions() | ||
| 276 | + | ||
| 277 | + # further split each proposed partition by union-find connectivity | ||
| 278 | + partition_nodes_list: list[dict[Node, None]] = [ | ||
| 279 | + sp_nodes | ||
| 280 | + for partition in partitions | ||
| 281 | + for sp_nodes in split_partition_with_union_find(partition.nodes) | ||
| 282 | + ] | ||
| 283 | + | ||
| 284 | + for partition_nodes in partition_nodes_list: | ||
| 285 | + fused_id = self.fused_id_ | ||
| 286 | + self.fused_id_ += 1 | ||
| 287 | + | ||
| 288 | + fused_name = f"dvm_graph_fused_{fused_id}" | ||
| 289 | + sorted_nodes = topo_sort(list(partition_nodes)) | ||
| 290 | + | ||
| 291 | + sub_gm, orig_inputs, orig_outputs = fuse_as_graphmodule( | ||
| 292 | + self.graph_module, | ||
| 293 | + sorted_nodes, | ||
| 294 | + fused_name, | ||
| 295 | + partition_nodes, | ||
| 296 | + ) | ||
| 297 | + | ||
| 298 | + # post-processing inside sub graph | ||
| 299 | + decompose_k1_matmul_to_mul(sub_gm) | ||
| 300 | + insert_promote_cast_by_pos_prims(sub_gm) | ||
| 301 | + insert_sum_fp32_prepost_cast_prims(sub_gm) | ||
| 302 | + expand_to_reshape(sub_gm) | ||
| 303 | + | ||
| 304 | + if not self._should_fuse(sub_gm, orig_outputs, orig_inputs): | ||
| 305 | + continue | ||
| 306 | + | ||
| 307 | + _fused_metas[fused_id] = _FusedMeta(fused_name, sub_gm, orig_inputs) | ||
| 308 | + | ||
| 309 | + output_len = len(orig_outputs) | ||
| 310 | + custom = self._get_or_create_custom_op(orig_inputs, output_len) | ||
| 311 | + | ||
| 312 | + # create fused call node in original graph | ||
| 313 | + args = (*orig_inputs, fused_id) | ||
| 314 | + new_node = self.graph_module.graph.call_function( | ||
| 315 | + custom._opoverload, tuple(args), None | ||
| 316 | + ) | ||
| 317 | + | ||
| 318 | + new_meta_vals = [] | ||
| 319 | + with V.fake_mode: | ||
| 320 | + for orig_output in orig_outputs: | ||
| 321 | + meta_val = orig_output.meta["val"] | ||
| 322 | + new_meta_vals.append( | ||
| 323 | + torch.empty( | ||
| 324 | + meta_val.size(), | ||
| 325 | + dtype=meta_val.dtype, | ||
| 326 | + device=meta_val.device, | ||
| 327 | + requires_grad=meta_val.requires_grad, | ||
| 328 | + ) | ||
| 329 | + ) | ||
| 330 | + | ||
| 331 | + if output_len == 1: | ||
| 332 | + orig_outputs[0].replace_all_uses_with(new_node) | ||
| 333 | + new_node.meta["val"] = new_meta_vals[0] | ||
| 334 | + else: | ||
| 335 | + for i, (orig_output, meta_val) in enumerate( | ||
| 336 | + zip(orig_outputs, new_meta_vals) | ||
| 337 | + ): | ||
| 338 | + proxy_out = torch.fx.Proxy(new_node)[i].node | ||
| 339 | + proxy_out.meta["val"] = meta_val | ||
| 340 | + orig_output.replace_all_uses_with(proxy_out) | ||
| 341 | + new_node.meta["val"] = tuple(new_meta_vals) | ||
| 342 | + | ||
| 343 | + # erase old nodes | ||
| 344 | + erase_nodes(self.graph_module, sorted_nodes) | ||
| 345 | + | ||
| 346 | + legalize_graph(self.graph_module) | ||
| 347 | + output_node = self.graph_module.graph.find_nodes(op="output")[0] | ||
| 348 | + next(iter(reversed(self.graph_module.graph.nodes))).append(output_node) | ||
| 349 | + return self.graph_module | ||
| 350 | + | ||
| 351 | + | ||
| 352 | +def dvm_graph_fusion(graph: Graph): | ||
| 353 | + gm: GraphModule = graph.owning_module | ||
| 354 | + | ||
| 355 | + dvm_support = DvmOpSupport() | ||
| 356 | + fusion_part = GraphFusionPartitioner( | ||
| 357 | + gm, | ||
| 358 | + dvm_support, | ||
| 359 | + allows_single_node_partition=True, | ||
| 360 | + ) | ||
| 361 | + fusion_part.partition_and_fuse() | ||
| 362 | + | ||
| 363 | + | ||
| 364 | +def _dvm_generate_fallback_kernel(self, fallback_kernel): | ||
| 365 | + """ | ||
| 366 | + Patch point: PythonWrapperCodegen.generate_fallback_kernel | ||
| 367 | + If it's our custom op, pop meta and emit dvm kernel code. | ||
| 368 | + """ | ||
| 369 | + args = [*fallback_kernel.codegen_args(), *fallback_kernel.codegen_kwargs()] | ||
| 370 | + | ||
| 371 | + if not fallback_kernel.op_overload._name.startswith("dvm::fused_graph_"): | ||
| 372 | + DvmGraphFusionPatch._orig_generate_fallback_kernel(self, fallback_kernel) | ||
| 373 | + return | ||
| 374 | + | ||
| 375 | + fused_id = int(args[-1]) | ||
| 376 | + meta = _fused_metas.pop(fused_id) | ||
| 377 | + | ||
| 378 | + args_list = list(args[:-1]) | ||
| 379 | + if not is_fx_dynamic(meta.gm): | ||
| 380 | + patch_gm_placeholder_strides_from_codegen_args(meta.gm, args_list) | ||
| 381 | + cg, code = meta.codegen() | ||
| 382 | + self.header.splice(code) | ||
| 383 | + | ||
| 384 | + buf_name = fallback_kernel.get_name() | ||
| 385 | + | ||
| 386 | + args_list = list(args[:-1]) | ||
| 387 | + # cont/trans handling based on codegen interpreter | ||
| 388 | + for i, skip_cont in enumerate(cg.cont_flag_input): | ||
| 389 | + if not skip_cont: | ||
| 390 | + args_list[i] += ".contiguous()" | ||
| 391 | + for i, trans in enumerate(cg.need_trans_input): | ||
| 392 | + if trans: | ||
| 393 | + args_list[i] += ".mT" | ||
| 394 | + | ||
| 395 | + self.writeline(f"{buf_name} = {meta.name}({', '.join(args_list)})") | ||
| 396 | + self.add_import_once("from torch_npu._inductor import dvm") | ||
| 397 | + | ||
| 398 | + | ||
| 399 | +class DvmGraphFusionPatch: | ||
| 400 | + _enabled = False | ||
| 401 | + _orig_generate_fallback_kernel = None | ||
| 402 | + _orig_post_grad_custom_post_pass = None | ||
| 403 | + | ||
| 404 | + | ||
| 405 | + def enable() -> None: | ||
| 406 | + if not DvmGraphFusionPatch._enabled: | ||
| 407 | + DvmGraphFusionPatch._orig_generate_fallback_kernel = ( | ||
| 408 | + PythonWrapperCodegen.generate_fallback_kernel | ||
| 409 | + ) | ||
| 410 | + DvmGraphFusionPatch._orig_post_grad_custom_post_pass = ( | ||
| 411 | + inductor_config.post_grad_custom_post_pass | ||
| 412 | + ) | ||
| 413 | + PythonWrapperCodegen.generate_fallback_kernel = ( | ||
| 414 | + _dvm_generate_fallback_kernel | ||
| 415 | + ) | ||
| 416 | + inductor_config.post_grad_custom_post_pass = dvm_graph_fusion | ||
| 417 | + DvmGraphFusionPatch._enabled = True | ||
| 418 | + | ||
| 419 | + | ||
| 420 | + def disable() -> None: | ||
| 421 | + if not DvmGraphFusionPatch._enabled: | ||
| 422 | + return | ||
| 423 | + PythonWrapperCodegen.generate_fallback_kernel = ( | ||
| 424 | + DvmGraphFusionPatch._orig_generate_fallback_kernel | ||
| 425 | + ) | ||
| 426 | + inductor_config.post_grad_custom_post_pass = ( | ||
| 427 | + DvmGraphFusionPatch._orig_post_grad_custom_post_pass | ||
| 428 | + ) | ||
| 429 | + DvmGraphFusionPatch._enabled = False | ||
| 430 | + | ||
| 431 | + def __enter__(self) -> "DvmGraphFusionPatch": | ||
| 432 | + DvmGraphFusionPatch.enable() | ||
| 433 | + return self | ||
| 434 | + | ||
| 435 | + def __exit__(self, exc_type, exc, tb) -> bool: | ||
| 436 | + DvmGraphFusionPatch.disable() | ||
| 437 | + return False | ||
| @@ -0,0 +1,395 @@ | |||
| 1 | +import os | ||
| 2 | + | ||
| 3 | +import torch | ||
| 4 | +from torch._inductor import config | ||
| 5 | +from torch._inductor.fx_passes.control_dependencies import control_deps | ||
| 6 | +from torch._inductor.codegen.common import IndentedBuffer | ||
| 7 | +from torch._inductor.codegen.simd import code_hash, SIMDKernel | ||
| 8 | +from torch._inductor.scheduler import WhyNoFuse | ||
| 9 | +from torch._inductor.utils import get_fused_kernel_name | ||
| 10 | +from torch._inductor.virtualized import V | ||
| 11 | +from torch_npu._inductor.ascend_npu_ir.ascend_npu_ir import config as anir_config | ||
| 12 | +from torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.codegen.mlir import ( | ||
| 13 | + NpuMlirKernel, | ||
| 14 | + NpuMlirScheduling, | ||
| 15 | +) | ||
| 16 | +from torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch import ( | ||
| 17 | + lowering as npu_lowering, | ||
| 18 | +) | ||
| 19 | +from torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.utils import ( | ||
| 20 | + get_num_call_functions, | ||
| 21 | + to_folder, | ||
| 22 | +) | ||
| 23 | + | ||
| 24 | +from .decomp import patch_decomp | ||
| 25 | +from .fx_test import generate_dvm_fx_case | ||
| 26 | +from .graph_build import DvmCodegenInterpreter | ||
| 27 | +from .op_emitter import common_rule, DVM_OP_REGISTRY, DVM_SUPPORT_TYPE, _extra_int_types | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +dump_fx_test = os.environ.get("INDUCTOR_DVM_DUMP_FX_TEST", "0") == "1" | ||
| 31 | +view_fusion_level = int(os.environ.get("INDUCTOR_DVM_VIEW_FUSION_LEVEL", "1")) | ||
| 32 | +disable_post_reduce_fusion = ( | ||
| 33 | + os.environ.get("INDUCTOR_DVM_DISABLE_POST_REDUCE_FUSION", "0") == "1" | ||
| 34 | +) | ||
| 35 | +aten = torch.ops.aten | ||
| 36 | +prims = torch.ops.prims | ||
| 37 | +quantized = torch.ops.quantized | ||
| 38 | +_quantized = torch.ops._quantized | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +anir_config.GENERATE_LIST = [ | ||
| 42 | + control_deps, | ||
| 43 | + aten._assert_scalar, | ||
| 44 | + aten.mul, | ||
| 45 | + aten.add, | ||
| 46 | + aten.sub, | ||
| 47 | + aten.div, | ||
| 48 | + aten.clamp_min, | ||
| 49 | + aten.clamp_max, | ||
| 50 | + aten.maximum, | ||
| 51 | + aten.minimum, | ||
| 52 | + aten.abs, | ||
| 53 | + aten.reciprocal, | ||
| 54 | + aten.log, | ||
| 55 | + aten.exp, | ||
| 56 | + aten.pow, | ||
| 57 | + aten.sqrt, | ||
| 58 | + aten.rsqrt, | ||
| 59 | + aten.neg, | ||
| 60 | + aten.lt, | ||
| 61 | + aten.le, | ||
| 62 | + aten.gt, | ||
| 63 | + aten.ge, | ||
| 64 | + aten.eq, | ||
| 65 | + aten.ne, | ||
| 66 | + aten.bitwise_and, | ||
| 67 | + aten.bitwise_or, | ||
| 68 | + aten.bitwise_not, | ||
| 69 | + aten.where, | ||
| 70 | + prims.convert_element_type, | ||
| 71 | + torch.ops.npu.npu_dtype_cast, | ||
| 72 | + torch.ops.npu.npu_dtype_cast_backward, | ||
| 73 | + torch.ops.npu._npu_dtype_cast, | ||
| 74 | + torch.ops.npu._npu_dtype_cast_backward, | ||
| 75 | + aten.expand, | ||
| 76 | + aten.var_mean, | ||
| 77 | + aten.sum, | ||
| 78 | + aten.mean, | ||
| 79 | + aten.amax, | ||
| 80 | + aten.amin, | ||
| 81 | + aten.full, | ||
| 82 | + aten.relu, | ||
| 83 | + aten.where, | ||
| 84 | + aten.scalar_tensor, | ||
| 85 | + aten.unsqueeze, | ||
| 86 | + aten.squeeze, | ||
| 87 | + aten.reshape, | ||
| 88 | + # aten.copy, | ||
| 89 | + # aten.copy_, | ||
| 90 | + # aten.clone, | ||
| 91 | +] | ||
| 92 | + | ||
| 93 | +def _is_node_supported_by_dvm_rule(node, allow_common_rule=False): | ||
| 94 | + if node.target in DVM_OP_REGISTRY: | ||
| 95 | + _, rule = DVM_OP_REGISTRY.get(node.target) | ||
| 96 | + return rule(node) | ||
| 97 | + return allow_common_rule and common_rule(node) | ||
| 98 | + | ||
| 99 | + | ||
| 100 | +def _codegen_dvm_kernel(self, Name=None): | ||
| 101 | + def is_node_dvm_supported(node): | ||
| 102 | + if node.op == "placeholder": | ||
| 103 | + meta = node.meta["val"] | ||
| 104 | + if isinstance(meta, torch._subclasses.FakeTensor): | ||
| 105 | + return meta.dtype in [*DVM_SUPPORT_TYPE, *_extra_int_types] | ||
| 106 | + if node.op == "call_function": | ||
| 107 | + return _is_node_supported_by_dvm_rule(node) | ||
| 108 | + return True | ||
| 109 | + | ||
| 110 | + if all(is_node_dvm_supported(node) for node in self._gm.graph.nodes): | ||
| 111 | + self.dvm_codegen = DvmCodegenInterpreter( | ||
| 112 | + self._gm, ktype="vector", view_fusion_level=view_fusion_level | ||
| 113 | + ) | ||
| 114 | + self.dvm_codegen.run() | ||
| 115 | + return self.dvm_codegen.code.getvalue() | ||
| 116 | + else: | ||
| 117 | + self.dvm_codegen = None | ||
| 118 | + return self._gm.print_readable(print_output=False) | ||
| 119 | + | ||
| 120 | + | ||
| 121 | +def _kernel_layout_key(mlir_kernel): | ||
| 122 | + non_contiguous_key = tuple( | ||
| 123 | + (name, tuple(indices)) | ||
| 124 | + for name, indices in sorted(mlir_kernel.non_contiguous_indices.items()) | ||
| 125 | + ) | ||
| 126 | + dvm_codegen = getattr(mlir_kernel, "dvm_codegen", None) | ||
| 127 | + cont_flag_input = tuple(getattr(dvm_codegen, "cont_flag_input", ())) | ||
| 128 | + return non_contiguous_key, cont_flag_input | ||
| 129 | + | ||
| 130 | + | ||
| 131 | +def _define_dvm_kernel(self, src_code, mlir_kernel, traced_graph, mode=None): | ||
| 132 | + kernel_key = (src_code, _kernel_layout_key(mlir_kernel)) | ||
| 133 | + wrapper = V.graph.wrapper_code | ||
| 134 | + | ||
| 135 | + if kernel_key in wrapper.src_to_kernel: | ||
| 136 | + kernel_name = wrapper.src_to_kernel[kernel_key] | ||
| 137 | + else: | ||
| 138 | + fused_kernel_name = "dvm_" + get_fused_kernel_name( | ||
| 139 | + mlir_kernel._snodes, config.triton.descriptive_names | ||
| 140 | + ) | ||
| 141 | + kernel_suffix = V.graph.wrapper_code.next_kernel_suffix() | ||
| 142 | + kernel_name = "_".join([fused_kernel_name, kernel_suffix]) | ||
| 143 | + | ||
| 144 | + traced_graph_hash = code_hash( | ||
| 145 | + traced_graph.print_readable(print_output=False) + kernel_name | ||
| 146 | + ) | ||
| 147 | + | ||
| 148 | + kernel_info = {} | ||
| 149 | + | ||
| 150 | + wrapper.src_to_kernel[kernel_key] = kernel_name | ||
| 151 | + current_device = V.graph.get_current_device_or_throw() | ||
| 152 | + | ||
| 153 | + compile_wrapper = IndentedBuffer() | ||
| 154 | + if ( | ||
| 155 | + mlir_kernel.dvm_codegen is None | ||
| 156 | + or kernel_name in anir_config.force_fallback_kernel_names | ||
| 157 | + ): | ||
| 158 | + num_call_functions = get_num_call_functions(mlir_kernel._gm) | ||
| 159 | + kernel_meta = { | ||
| 160 | + "device_str": current_device.type, | ||
| 161 | + "device_index": current_device.index, | ||
| 162 | + "num_outputs": mlir_kernel.num_outputs, | ||
| 163 | + "non_contiguous_indices": mlir_kernel.non_contiguous_indices, | ||
| 164 | + "dynamic": mlir_kernel._is_dynamic, | ||
| 165 | + "mutated_indices": mlir_kernel.mutated_indices, | ||
| 166 | + "traced_graph_cache": anir_config.traced_graph_cache, | ||
| 167 | + "traced_graph_hash": traced_graph_hash, | ||
| 168 | + "num_call_functions": num_call_functions, | ||
| 169 | + **kernel_info, | ||
| 170 | + } | ||
| 171 | + compile_wrapper.writeline( | ||
| 172 | + f"async_compile.import_fx({kernel_name!r}, kernel_meta={kernel_meta})" | ||
| 173 | + ) | ||
| 174 | + metadata_comment = ( | ||
| 175 | + f'"""\n{mlir_kernel._gm.print_readable(print_output=False)}\n"""' | ||
| 176 | + ) | ||
| 177 | + wrapper.define_kernel( | ||
| 178 | + kernel_name, compile_wrapper.getvalue(), metadata_comment | ||
| 179 | + ) | ||
| 180 | + dump_path = os.path.join( | ||
| 181 | + os.getenv("TORCHINDUCTOR_CACHE_DIR"), | ||
| 182 | + anir_config.traced_graph_cache, | ||
| 183 | + str(current_device.index), | ||
| 184 | + traced_graph_hash, | ||
| 185 | + ) | ||
| 186 | + if not os.path.exists(dump_path): | ||
| 187 | + os.makedirs(dump_path, exist_ok=True) | ||
| 188 | + to_folder( | ||
| 189 | + mlir_kernel._gm, | ||
| 190 | + dump_path, | ||
| 191 | + graph_hash=traced_graph_hash, | ||
| 192 | + module_name=traced_graph_hash, | ||
| 193 | + ) | ||
| 194 | + else: | ||
| 195 | + wrapper.add_import_once("from torch_npu._inductor import dvm") | ||
| 196 | + if dump_fx_test: | ||
| 197 | + generate_dvm_fx_case(mlir_kernel._gm, fusion_type="mlir") | ||
| 198 | + kernel_meta = { | ||
| 199 | + "kernel_name": fused_kernel_name, | ||
| 200 | + "kernel_fullname": kernel_name, | ||
| 201 | + } | ||
| 202 | + code = mlir_kernel.dvm_codegen.code | ||
| 203 | + code.splice( | ||
| 204 | + f""" | ||
| 205 | + k.set_kernel_info( | ||
| 206 | + {kernel_meta.get("kernel_name")!r}, # kernel_name | ||
| 207 | + {kernel_meta.get("kernel_fullname")!r}, # kernel_fullname | ||
| 208 | + ) | ||
| 209 | + """, | ||
| 210 | + strip=True, | ||
| 211 | + ) | ||
| 212 | + func_name = kernel_name + "_build" | ||
| 213 | + func_code = code.getvalue().replace( | ||
| 214 | + mlir_kernel.dvm_codegen.KERNEL_NAME_PLACEHOLDER, func_name | ||
| 215 | + ) | ||
| 216 | + compile_wrapper.writeline(func_name) | ||
| 217 | + | ||
| 218 | + if anir_config.online_acc_comp: | ||
| 219 | + dump_path = os.path.join( | ||
| 220 | + os.getenv("TORCHINDUCTOR_CACHE_DIR"), | ||
| 221 | + anir_config.traced_graph_cache, | ||
| 222 | + str(current_device.index), | ||
| 223 | + traced_graph_hash, | ||
| 224 | + ) | ||
| 225 | + if not os.path.exists(dump_path): | ||
| 226 | + os.makedirs(dump_path, exist_ok=True) | ||
| 227 | + to_folder( | ||
| 228 | + mlir_kernel._gm, | ||
| 229 | + dump_path, | ||
| 230 | + graph_hash=traced_graph_hash, | ||
| 231 | + module_name=traced_graph_hash, | ||
| 232 | + ) | ||
| 233 | + compile_wrapper.writeline( | ||
| 234 | + f"{kernel_name}._acc_meta = {{" | ||
| 235 | + f"'traced_graph_hash': {traced_graph_hash!r}, " | ||
| 236 | + f"'traced_graph_cache': {anir_config.traced_graph_cache!r}, " | ||
| 237 | + f"'device_index': {current_device.index}, " | ||
| 238 | + f"'num_outputs': {mlir_kernel.num_outputs}}}" | ||
| 239 | + ) | ||
| 240 | + | ||
| 241 | + wrapper.define_kernel(kernel_name, compile_wrapper.getvalue(), func_code) | ||
| 242 | + | ||
| 243 | + return kernel_name | ||
| 244 | + | ||
| 245 | + | ||
| 246 | +def _dvm_can_fuse_vertical(self, node1, node2): | ||
| 247 | + _, (numel1, rnumel1) = node1.group | ||
| 248 | + _, (numel2, rnumel2) = node2.group | ||
| 249 | + why = WhyNoFuse(node1, node2) | ||
| 250 | + | ||
| 251 | + if node1.is_reduction(): | ||
| 252 | + return False | ||
| 253 | + | ||
| 254 | + if not node2.is_reduction(): | ||
| 255 | + return numel1 == numel2 and rnumel1 == rnumel2 | ||
| 256 | + else: | ||
| 257 | + if numel1 == numel2 * rnumel2: | ||
| 258 | + if not all( | ||
| 259 | + SIMDKernel.is_compatible((numel2, rnumel2), n.get_ranges()) | ||
| 260 | + for n in node1.get_nodes() | ||
| 261 | + ): | ||
| 262 | + why("nodes numel/rnumel incompatibility") | ||
| 263 | + return False | ||
| 264 | + | ||
| 265 | + return True | ||
| 266 | + if numel1 != numel2: | ||
| 267 | + why("nodes numel incompatibility") | ||
| 268 | + return numel1 == numel2 | ||
| 269 | + | ||
| 270 | + | ||
| 271 | +def _dvm_can_fuse_horizontal(self, node1, node2): | ||
| 272 | + return False | ||
| 273 | + | ||
| 274 | + | ||
| 275 | +def _patch_lowering_type_checks(): | ||
| 276 | + import torch._inductor.graph as inductor_graph | ||
| 277 | + import torch._inductor.lowering as inductor_lowering | ||
| 278 | + import torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.lowering as npu_lowering_mod | ||
| 279 | + import torch_npu._inductor.graph as npu_graph_mod | ||
| 280 | + | ||
| 281 | + fallback_node_due_to_unsupported_type = ( | ||
| 282 | + inductor_lowering.fallback_node_due_to_unsupported_type | ||
| 283 | + ) | ||
| 284 | + | ||
| 285 | + def _fallback_node_due_to_unsupported_type( | ||
| 286 | + node: torch.fx.Node, allow_cpu_inputs=True | ||
| 287 | + ): | ||
| 288 | + if fallback_node_due_to_unsupported_type(node, allow_cpu_inputs): | ||
| 289 | + return True | ||
| 290 | + | ||
| 291 | + if node.target is torch.ops.higher_order.triton_kernel_wrapper_functional: | ||
| 292 | + return False | ||
| 293 | + if node.target is torch.ops.higher_order.triton_kernel_wrapper_mutation: | ||
| 294 | + return False | ||
| 295 | + if node.target is aten.lift_fresh_copy.default: | ||
| 296 | + return False | ||
| 297 | + | ||
| 298 | + return not _is_node_supported_by_dvm_rule(node, allow_common_rule=True) | ||
| 299 | + | ||
| 300 | + inductor_lowering.fallback_node_due_to_unsupported_type = ( | ||
| 301 | + _fallback_node_due_to_unsupported_type | ||
| 302 | + ) | ||
| 303 | + npu_lowering_mod.fallback_node_due_to_unsupported_type = ( | ||
| 304 | + _fallback_node_due_to_unsupported_type | ||
| 305 | + ) | ||
| 306 | + inductor_graph.fallback_node_due_to_unsupported_type = ( | ||
| 307 | + _fallback_node_due_to_unsupported_type | ||
| 308 | + ) | ||
| 309 | + npu_graph_mod.fallback_node_due_to_unsupported_type = ( | ||
| 310 | + _fallback_node_due_to_unsupported_type | ||
| 311 | + ) | ||
| 312 | + | ||
| 313 | + | ||
| 314 | +def _patch_lowering(): | ||
| 315 | + from torch_npu._inductor.ascend_npu_ir.ascend_npu_ir.npu.inductor_patch.lowering import ( | ||
| 316 | + is_boolean_dtype, | ||
| 317 | + is_integer_dtype, | ||
| 318 | + make_reduction, | ||
| 319 | + to_dtype, | ||
| 320 | + ) | ||
| 321 | + | ||
| 322 | + def get_overloads(aten_fn): | ||
| 323 | + if not isinstance(aten_fn, (list, tuple)): | ||
| 324 | + aten_fn = [aten_fn] | ||
| 325 | + else: | ||
| 326 | + aten_fn = list(aten_fn) | ||
| 327 | + | ||
| 328 | + for fn in list(aten_fn): | ||
| 329 | + if isinstance(fn, torch._ops.OpOverloadPacket): | ||
| 330 | + for overload in fn.overloads(): | ||
| 331 | + other_fn = getattr(fn, overload) | ||
| 332 | + aten_fn.append(other_fn) | ||
| 333 | + | ||
| 334 | + return aten_fn | ||
| 335 | + | ||
| 336 | + def sum_(x, axis=None, keepdims=False, *, dtype=None): | ||
| 337 | + if axis and any(ax < 0 for ax in axis): | ||
| 338 | + offset = len(x.get_size()) | ||
| 339 | + axis = [ax + offset if ax < 0 else ax for ax in axis] | ||
| 340 | + if ( | ||
| 341 | + is_integer_dtype(x.get_dtype()) or is_boolean_dtype(x.get_dtype()) | ||
| 342 | + ) and dtype is None: | ||
| 343 | + dtype = torch.int64 | ||
| 344 | + | ||
| 345 | + out_dtype = x.get_dtype() if dtype is None else dtype | ||
| 346 | + | ||
| 347 | + fn = make_reduction("sum", override_return_dtype=torch.float32) | ||
| 348 | + r = fn(x, axis, keepdims, dtype=torch.float32) | ||
| 349 | + | ||
| 350 | + if out_dtype != torch.float32: | ||
| 351 | + r = to_dtype(r, out_dtype) | ||
| 352 | + | ||
| 353 | + return r | ||
| 354 | + | ||
| 355 | + anir_config.disable_any_pbr = False | ||
| 356 | + ops = get_overloads([aten.sum, prims.sum]) | ||
| 357 | + npu_lowering.register_lowering(ops)(sum_) | ||
| 358 | + npu_lowering.make_fallback( | ||
| 359 | + aten.matmul_backward.default, | ||
| 360 | + layout_constraint=None, | ||
| 361 | + warn=False, | ||
| 362 | + override_decomp=True, | ||
| 363 | + ) | ||
| 364 | + npu_lowering.add_layout_constraint(aten.matmul_backward.default, None) | ||
| 365 | + | ||
| 366 | + | ||
| 367 | +class DvmMlirFusionPatch: | ||
| 368 | + _enabled = False | ||
| 369 | + | ||
| 370 | + | ||
| 371 | + def enable() -> None: | ||
| 372 | + if DvmMlirFusionPatch._enabled: | ||
| 373 | + return | ||
| 374 | + from torch._dynamo import config as dynamo_config | ||
| 375 | + from torch._inductor import config as inductor_config | ||
| 376 | + | ||
| 377 | + dynamo_config.specialize_float = True # enable float specialization until launch with scalar supported | ||
| 378 | + inductor_config.unroll_reductions_threshold = 1 # disable unroll reductions | ||
| 379 | + inductor_config.size_asserts = ( | ||
| 380 | + False # npu ops always return contiguous tensors which maybe different from meta outputs | ||
| 381 | + ) | ||
| 382 | + inductor_config.allow_buffer_reuse = False | ||
| 383 | + inductor_config.comprehensive_padding = False | ||
| 384 | + patch_decomp() | ||
| 385 | + _patch_lowering_type_checks() | ||
| 386 | + _patch_lowering() | ||
| 387 | + NpuMlirKernel.codegen_kernel = _codegen_dvm_kernel | ||
| 388 | + NpuMlirScheduling.define_kernel = _define_dvm_kernel | ||
| 389 | + if disable_post_reduce_fusion: | ||
| 390 | + NpuMlirScheduling.can_fuse_horizontal = _dvm_can_fuse_horizontal | ||
| 391 | + NpuMlirScheduling.can_fuse_vertical = _dvm_can_fuse_vertical | ||
| 392 | + DvmMlirFusionPatch._enabled = True | ||
| 393 | + | ||
| 394 | + | ||
| 395 | +DvmMlirFusionPatch.enable() | ||
| @@ -0,0 +1,577 @@ | |||
| 1 | +import torch | ||
| 2 | +import torch.fx | ||
| 3 | +import torch.utils._pytree as pytree | ||
| 4 | + | ||
| 5 | +from . import is_ascend950 | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +aten = torch.ops.aten | ||
| 9 | +prims = torch.ops.prims | ||
| 10 | + | ||
| 11 | +DVM_OP_REGISTRY = {} | ||
| 12 | + | ||
| 13 | +DVM_SUPPORT_TYPE = [ | ||
| 14 | + torch.bfloat16, | ||
| 15 | + torch.float16, | ||
| 16 | + torch.float32, | ||
| 17 | + torch.int32, | ||
| 18 | + torch.bool, | ||
| 19 | +] | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +DVM_SUPPORT_FLOAT_TYPE = [ | ||
| 23 | + torch.bfloat16, | ||
| 24 | + torch.float16, | ||
| 25 | + torch.float32, | ||
| 26 | +] | ||
| 27 | + | ||
| 28 | +_extra_int_types = [torch.int32] if is_ascend950 else [torch.int32, torch.int64] | ||
| 29 | +DVM_SUPPORT_FLOAT_INT_TYPE = [ | ||
| 30 | + *DVM_SUPPORT_FLOAT_TYPE, | ||
| 31 | + *_extra_int_types, | ||
| 32 | +] | ||
| 33 | + | ||
| 34 | +DVM_SUPPORT_OPTIONAL_INT64_TYPE = DVM_SUPPORT_TYPE if is_ascend950 else [*DVM_SUPPORT_TYPE, torch.int64] | ||
| 35 | + | ||
| 36 | +DVM_DTYPE_MAP = { | ||
| 37 | + torch.bfloat16: "dvm.bfloat16", | ||
| 38 | + torch.float16: "dvm.float16", | ||
| 39 | + torch.float32: "dvm.float32", | ||
| 40 | + torch.int32: "dvm.int32", | ||
| 41 | + torch.int64: "dvm.int64", | ||
| 42 | + torch.bool: "dvm.bool_", | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +def to_dvm_dtype(dtype): | ||
| 47 | + if dtype is None: | ||
| 48 | + return None | ||
| 49 | + if isinstance(dtype, torch.dtype): | ||
| 50 | + if dtype not in DVM_DTYPE_MAP: | ||
| 51 | + raise NotImplementedError(f"Unsupported dtype for DVM: {dtype}") | ||
| 52 | + return DVM_DTYPE_MAP[dtype] | ||
| 53 | + return dtype | ||
| 54 | + | ||
| 55 | + | ||
| 56 | +def _check_dtype(inputs, supported_dtypes): | ||
| 57 | + for inp in inputs: | ||
| 58 | + if not isinstance(inp, torch.fx.Node): | ||
| 59 | + continue | ||
| 60 | + if "val" not in inp.meta: | ||
| 61 | + continue | ||
| 62 | + | ||
| 63 | + for meta in pytree.tree_leaves(inp.meta["val"]): | ||
| 64 | + if not isinstance(meta, torch._subclasses.FakeTensor): | ||
| 65 | + continue | ||
| 66 | + if meta.dtype not in supported_dtypes: | ||
| 67 | + return False | ||
| 68 | + return True | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +def where_rule(node: torch.fx.Node): | ||
| 72 | + return _check_dtype(node.args[1:], DVM_SUPPORT_FLOAT_INT_TYPE) | ||
| 73 | + | ||
| 74 | + | ||
| 75 | +def _is_last2_transpose_tensor(t: torch._subclasses.FakeTensor) -> bool: | ||
| 76 | + if t.dim() < 2: | ||
| 77 | + return False | ||
| 78 | + | ||
| 79 | + if t.is_contiguous(): | ||
| 80 | + return False | ||
| 81 | + | ||
| 82 | + if not (t.stride(-2) == 1 and t.stride(-1) == t.size(-2)): | ||
| 83 | + return False | ||
| 84 | + | ||
| 85 | + batch = t.size(-1) * t.size(-2) | ||
| 86 | + for d in range(t.dim() - 3, -1, -1): | ||
| 87 | + if t.stride(d) != batch: | ||
| 88 | + return False | ||
| 89 | + batch *= t.size(d) | ||
| 90 | + | ||
| 91 | + return True | ||
| 92 | + | ||
| 93 | + | ||
| 94 | +def mm_rule(node: torch.fx.Node): | ||
| 95 | + UINT16_MAX = (1 << 16) - 1 | ||
| 96 | + UINT8_MAX = (1 << 8) - 1 | ||
| 97 | + MAX_INNER = UINT16_MAX - UINT8_MAX | ||
| 98 | + SMALL_OUTPUT_MAX = 256 | ||
| 99 | + | ||
| 100 | + def inner_axis_length(t: torch._subclasses.FakeTensor): | ||
| 101 | + if _is_last2_transpose_tensor(t): | ||
| 102 | + return t.mT.size(-1) | ||
| 103 | + return t.size(-1) | ||
| 104 | + | ||
| 105 | + def check(input_node): | ||
| 106 | + t = input_node.meta["val"] | ||
| 107 | + if t.dim() > 4 or t.dim() < 2: | ||
| 108 | + return False | ||
| 109 | + inner_axis = inner_axis_length(t) | ||
| 110 | + if not is_ascend950: | ||
| 111 | + if isinstance(inner_axis, torch.SymInt): | ||
| 112 | + return False | ||
| 113 | + if inner_axis > MAX_INNER: | ||
| 114 | + return False | ||
| 115 | + return True | ||
| 116 | + | ||
| 117 | + def check_output(output_node): | ||
| 118 | + t = output_node.meta["val"] | ||
| 119 | + last_two_dims = t.shape[-2:] | ||
| 120 | + if all(not isinstance(dim, torch.SymInt) for dim in last_two_dims) and all( | ||
| 121 | + dim <= SMALL_OUTPUT_MAX for dim in last_two_dims | ||
| 122 | + ): | ||
| 123 | + return False | ||
| 124 | + return True | ||
| 125 | + | ||
| 126 | + def check_k1_fusion(lhs_node, rhs_node): | ||
| 127 | + lhs_t = lhs_node.meta["val"] | ||
| 128 | + rhs_t = rhs_node.meta["val"] | ||
| 129 | + lhs_k = lhs_t.shape[-1] | ||
| 130 | + rhs_k = rhs_t.shape[-2] | ||
| 131 | + if isinstance(lhs_k, torch.SymInt) or isinstance(rhs_k, torch.SymInt): | ||
| 132 | + return True | ||
| 133 | + if lhs_k == 1 and rhs_k == 1: | ||
| 134 | + return (not _is_last2_transpose_tensor(lhs_t)) and ( | ||
| 135 | + not _is_last2_transpose_tensor(rhs_t) | ||
| 136 | + ) | ||
| 137 | + return True | ||
| 138 | + | ||
| 139 | + if node.target in [aten.mm.default, aten.bmm.default]: | ||
| 140 | + lhs = node.args[0] | ||
| 141 | + rhs = node.args[1] | ||
| 142 | + elif node.target is aten.addmm.default: | ||
| 143 | + lhs = node.args[1] | ||
| 144 | + rhs = node.args[2] | ||
| 145 | + else: | ||
| 146 | + return False | ||
| 147 | + if node.meta["val"].dtype not in (torch.float16, torch.bfloat16): | ||
| 148 | + return False | ||
| 149 | + | ||
| 150 | + return ( | ||
| 151 | + check(lhs) and check(rhs) and check_output(node) and check_k1_fusion(lhs, rhs) | ||
| 152 | + ) | ||
| 153 | + | ||
| 154 | + | ||
| 155 | +class DvmOpInfo: | ||
| 156 | + def __init__( | ||
| 157 | + self, | ||
| 158 | + func, | ||
| 159 | + input_dtypes=DVM_SUPPORT_FLOAT_TYPE, | ||
| 160 | + output_dtypes=DVM_SUPPORT_FLOAT_TYPE, | ||
| 161 | + rule=None, | ||
| 162 | + ): | ||
| 163 | + self.func = func | ||
| 164 | + self.input_dtypes = input_dtypes | ||
| 165 | + self.output_dtypes = output_dtypes | ||
| 166 | + self.rule = rule | ||
| 167 | + | ||
| 168 | + def is_supported(self, node: torch.fx.Node): | ||
| 169 | + inputs = pytree.arg_tree_leaves(*node.args, **node.kwargs) | ||
| 170 | + return ( | ||
| 171 | + ( | ||
| 172 | + self.input_dtypes is None | ||
| 173 | + or _check_dtype(inputs, self.input_dtypes) | ||
| 174 | + ) | ||
| 175 | + and ( | ||
| 176 | + self.output_dtypes is None | ||
| 177 | + or _check_dtype([node], self.output_dtypes) | ||
| 178 | + ) | ||
| 179 | + and (self.rule is None or self.rule(node)) | ||
| 180 | + ) | ||
| 181 | + | ||
| 182 | + def __iter__(self): | ||
| 183 | + yield self.func | ||
| 184 | + yield self.is_supported | ||
| 185 | + | ||
| 186 | + | ||
| 187 | +def register_dvm_op( | ||
| 188 | + *ops, | ||
| 189 | + input_dtypes=DVM_SUPPORT_FLOAT_TYPE, | ||
| 190 | + output_dtypes=DVM_SUPPORT_FLOAT_TYPE, | ||
| 191 | + rule=None, | ||
| 192 | +): | ||
| 193 | + def decorator(func): | ||
| 194 | + info = DvmOpInfo( | ||
| 195 | + func, | ||
| 196 | + input_dtypes=input_dtypes, | ||
| 197 | + output_dtypes=output_dtypes, | ||
| 198 | + rule=rule, | ||
| 199 | + ) | ||
| 200 | + for op in ops: | ||
| 201 | + DVM_OP_REGISTRY[op] = info | ||
| 202 | + return func | ||
| 203 | + | ||
| 204 | + return decorator | ||
| 205 | + | ||
| 206 | + | ||
| 207 | +_DEFAULT_OP_INFO = DvmOpInfo(None) | ||
| 208 | + | ||
| 209 | + | ||
| 210 | +def common_rule(node: torch.fx.Node): | ||
| 211 | + return _DEFAULT_OP_INFO.is_supported(node) | ||
| 212 | + | ||
| 213 | + | ||
| 214 | +def format_shape(shape): | ||
| 215 | + if isinstance(shape, (int, torch.SymInt)): | ||
| 216 | + shape = [shape] | ||
| 217 | + return "[" + ", ".join(map(str, shape)) + "]" | ||
| 218 | + | ||
| 219 | + | ||
| 220 | + | ||
| 221 | + aten.add.Tensor, | ||
| 222 | + aten.add.Scalar, | ||
| 223 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 224 | + output_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 225 | +) | ||
| 226 | +def add(x, y, alpha=1): | ||
| 227 | + if alpha != 1: | ||
| 228 | + y = mul(y, alpha) | ||
| 229 | + return f"k.add({x}, {y})" | ||
| 230 | + | ||
| 231 | + | ||
| 232 | + | ||
| 233 | + aten.sub.Tensor, | ||
| 234 | + aten.sub.Scalar, | ||
| 235 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 236 | + output_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 237 | +) | ||
| 238 | +def sub(x, y): | ||
| 239 | + return f"k.sub({x}, {y})" | ||
| 240 | + | ||
| 241 | + | ||
| 242 | + | ||
| 243 | + aten.mul.Tensor, | ||
| 244 | + aten.mul.Scalar, | ||
| 245 | + input_dtypes=[*DVM_SUPPORT_FLOAT_TYPE, torch.int32], | ||
| 246 | + output_dtypes=[*DVM_SUPPORT_FLOAT_TYPE, torch.int32], | ||
| 247 | +) | ||
| 248 | +def mul(x, y): | ||
| 249 | + return f"k.mul({x}, {y})" | ||
| 250 | + | ||
| 251 | + | ||
| 252 | + | ||
| 253 | +def div(x, y): | ||
| 254 | + return f"k.div({x}, {y})" | ||
| 255 | + | ||
| 256 | + | ||
| 257 | + | ||
| 258 | +def pow_op(x, y): | ||
| 259 | + return f"k.pow({x}, {y})" | ||
| 260 | + | ||
| 261 | + | ||
| 262 | + | ||
| 263 | + aten.lt.Tensor, | ||
| 264 | + aten.lt.Scalar, | ||
| 265 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 266 | + output_dtypes=[torch.bool], | ||
| 267 | +) | ||
| 268 | +def less(x, y): | ||
| 269 | + return f"k.less({x}, {y})" | ||
| 270 | + | ||
| 271 | + | ||
| 272 | + | ||
| 273 | + aten.le.Tensor, | ||
| 274 | + aten.le.Scalar, | ||
| 275 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 276 | + output_dtypes=[torch.bool], | ||
| 277 | +) | ||
| 278 | +def less_equal(x, y): | ||
| 279 | + return f"k.less_equal({x}, {y})" | ||
| 280 | + | ||
| 281 | + | ||
| 282 | + | ||
| 283 | + aten.gt.Tensor, | ||
| 284 | + aten.gt.Scalar, | ||
| 285 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 286 | + output_dtypes=[torch.bool], | ||
| 287 | +) | ||
| 288 | +def greater(x, y): | ||
| 289 | + return f"k.greater({x}, {y})" | ||
| 290 | + | ||
| 291 | + | ||
| 292 | + | ||
| 293 | + aten.ge.Tensor, | ||
| 294 | + aten.ge.Scalar, | ||
| 295 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 296 | + output_dtypes=[torch.bool], | ||
| 297 | +) | ||
| 298 | +def greater_equal(x, y): | ||
| 299 | + return f"k.greater_equal({x}, {y})" | ||
| 300 | + | ||
| 301 | + | ||
| 302 | + | ||
| 303 | +def maximum(x, y): | ||
| 304 | + return f"k.maximum({x}, {y})" | ||
| 305 | + | ||
| 306 | + | ||
| 307 | + | ||
| 308 | +def minimum(x, y): | ||
| 309 | + return f"k.minimum({x}, {y})" | ||
| 310 | + | ||
| 311 | + | ||
| 312 | + | ||
| 313 | +def clamp_min(x, min_value): | ||
| 314 | + return maximum(x, min_value) | ||
| 315 | + | ||
| 316 | + | ||
| 317 | + | ||
| 318 | +def clamp_max(x, max_value): | ||
| 319 | + return minimum(x, max_value) | ||
| 320 | + | ||
| 321 | + | ||
| 322 | + | ||
| 323 | + aten.logical_and.default, | ||
| 324 | + aten.bitwise_and.Tensor, | ||
| 325 | + input_dtypes=[torch.bool], | ||
| 326 | + output_dtypes=[torch.bool], | ||
| 327 | +) | ||
| 328 | +def logical_and(x, y): | ||
| 329 | + return f"k.logical_and({x}, {y})" | ||
| 330 | + | ||
| 331 | + | ||
| 332 | + | ||
| 333 | + aten.logical_or.default, | ||
| 334 | + aten.bitwise_or.Tensor, | ||
| 335 | + input_dtypes=[torch.bool], | ||
| 336 | + output_dtypes=[torch.bool], | ||
| 337 | +) | ||
| 338 | +def logical_or(x, y): | ||
| 339 | + return f"k.logical_or({x}, {y})" | ||
| 340 | + | ||
| 341 | + | ||
| 342 | + | ||
| 343 | + aten.eq.Tensor, | ||
| 344 | + aten.eq.Scalar, | ||
| 345 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 346 | + output_dtypes=[torch.bool], | ||
| 347 | +) | ||
| 348 | +def equal(x, y): | ||
| 349 | + return f"k.equal({x}, {y})" | ||
| 350 | + | ||
| 351 | + | ||
| 352 | + | ||
| 353 | + aten.ne.Tensor, | ||
| 354 | + aten.ne.Scalar, | ||
| 355 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 356 | + output_dtypes=[torch.bool], | ||
| 357 | +) | ||
| 358 | +def not_equal(x, y): | ||
| 359 | + return f"k.not_equal({x}, {y})" | ||
| 360 | + | ||
| 361 | + | ||
| 362 | + | ||
| 363 | +def sqrt(x): | ||
| 364 | + return f"k.sqrt({x})" | ||
| 365 | + | ||
| 366 | + | ||
| 367 | + | ||
| 368 | +def rsqrt(x): | ||
| 369 | + return div(1, sqrt(x)) | ||
| 370 | + | ||
| 371 | + | ||
| 372 | + | ||
| 373 | +def abs_op(x): | ||
| 374 | + return f"k.abs({x})" | ||
| 375 | + | ||
| 376 | + | ||
| 377 | + | ||
| 378 | +def log(x): | ||
| 379 | + return f"k.log({x})" | ||
| 380 | + | ||
| 381 | + | ||
| 382 | + | ||
| 383 | +def exp(x): | ||
| 384 | + return f"k.exp({x})" | ||
| 385 | + | ||
| 386 | + | ||
| 387 | + | ||
| 388 | +def reciprocal(x): | ||
| 389 | + return f"k.reciprocal({x})" | ||
| 390 | + | ||
| 391 | + | ||
| 392 | + | ||
| 393 | +def is_finite(x): | ||
| 394 | + return f"k.is_finite({x})" | ||
| 395 | + | ||
| 396 | + | ||
| 397 | + | ||
| 398 | + aten.logical_not.default, | ||
| 399 | + aten.bitwise_not.default, | ||
| 400 | + input_dtypes=[torch.bool], | ||
| 401 | + output_dtypes=[torch.bool], | ||
| 402 | +) | ||
| 403 | +def logical_not(x): | ||
| 404 | + return f"k.logical_not({x})" | ||
| 405 | + | ||
| 406 | + | ||
| 407 | + | ||
| 408 | +def round_op(x): | ||
| 409 | + return f"k.round({x})" | ||
| 410 | + | ||
| 411 | + | ||
| 412 | + | ||
| 413 | +def floor(x): | ||
| 414 | + return f"k.floor({x})" | ||
| 415 | + | ||
| 416 | + | ||
| 417 | + | ||
| 418 | +def ceil(x): | ||
| 419 | + return f"k.ceil({x})" | ||
| 420 | + | ||
| 421 | + | ||
| 422 | + | ||
| 423 | +def trunc(x): | ||
| 424 | + return f"k.trunc({x})" | ||
| 425 | + | ||
| 426 | + | ||
| 427 | + | ||
| 428 | + aten._to_copy.default, | ||
| 429 | + prims.convert_element_type.default, | ||
| 430 | + torch.ops.npu.npu_dtype_cast.default, | ||
| 431 | + torch.ops.npu.npu_dtype_cast_backward.default, | ||
| 432 | + torch.ops.npu._npu_dtype_cast.default, | ||
| 433 | + torch.ops.npu._npu_dtype_cast_backward.default, | ||
| 434 | + input_dtypes=DVM_SUPPORT_OPTIONAL_INT64_TYPE, | ||
| 435 | + output_dtypes=DVM_SUPPORT_OPTIONAL_INT64_TYPE, | ||
| 436 | +) | ||
| 437 | +def cast(x, dtype): | ||
| 438 | + dtype = to_dvm_dtype(dtype) | ||
| 439 | + return f"k.cast({x}, {dtype})" | ||
| 440 | + | ||
| 441 | + | ||
| 442 | + | ||
| 443 | + aten.expand.default, | ||
| 444 | + input_dtypes=DVM_SUPPORT_TYPE, | ||
| 445 | + output_dtypes=DVM_SUPPORT_TYPE, | ||
| 446 | +) | ||
| 447 | +def broadcast(x, shape): | ||
| 448 | + shape = format_shape(shape) | ||
| 449 | + return f"k.broadcast({x}, {shape})" | ||
| 450 | + | ||
| 451 | + | ||
| 452 | + | ||
| 453 | + aten.where.default, | ||
| 454 | + aten.where.self, | ||
| 455 | + input_dtypes=None, | ||
| 456 | + output_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 457 | + rule=where_rule, | ||
| 458 | +) | ||
| 459 | +def select(x, y, z): | ||
| 460 | + return f"k.select({x}, {y}, {z})" | ||
| 461 | + | ||
| 462 | + | ||
| 463 | + | ||
| 464 | +def reduce_sum(x, dim=None, keepdim=False, dtype=None): | ||
| 465 | + if dim is None: | ||
| 466 | + dim = [] | ||
| 467 | + dim = format_shape(dim) | ||
| 468 | + return f"k.sum({x}, {dim}, {keepdim})" | ||
| 469 | + | ||
| 470 | + | ||
| 471 | + | ||
| 472 | +def reduce_max(x, dim=None, keepdim=False): | ||
| 473 | + if dim is None: | ||
| 474 | + dim = [] | ||
| 475 | + dim = format_shape(dim) | ||
| 476 | + return f"k.max({x}, {dim}, {keepdim})" | ||
| 477 | + | ||
| 478 | + | ||
| 479 | + | ||
| 480 | +def reduce_min(x, dim=None, keepdim=False): | ||
| 481 | + if dim is None: | ||
| 482 | + dim = [] | ||
| 483 | + dim = format_shape(dim) | ||
| 484 | + return f"k.min({x}, {dim}, {keepdim})" | ||
| 485 | + | ||
| 486 | + | ||
| 487 | + | ||
| 488 | + aten.view.default, | ||
| 489 | + aten.reshape.default, | ||
| 490 | + aten._unsafe_view.default, | ||
| 491 | + input_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 492 | + output_dtypes=DVM_SUPPORT_FLOAT_INT_TYPE, | ||
| 493 | +) | ||
| 494 | +def reshape(x, shape): | ||
| 495 | + shape = format_shape(shape) | ||
| 496 | + return f"k.reshape({x}, {shape})" | ||
| 497 | + | ||
| 498 | + | ||
| 499 | + | ||
| 500 | +def neg(x): | ||
| 501 | + return mul(x, -1) | ||
| 502 | + | ||
| 503 | + | ||
| 504 | + | ||
| 505 | +def relu(x): | ||
| 506 | + return maximum(x, 0) | ||
| 507 | + | ||
| 508 | + | ||
| 509 | +def copy(x): | ||
| 510 | + return f"k.copy({x})" | ||
| 511 | + | ||
| 512 | + | ||
| 513 | + | ||
| 514 | + aten.copy.default, | ||
| 515 | + aten.copy_.default, | ||
| 516 | + input_dtypes=DVM_SUPPORT_TYPE, | ||
| 517 | + output_dtypes=DVM_SUPPORT_TYPE, | ||
| 518 | +) | ||
| 519 | +def copy_(dst, src, non_blocking=False): | ||
| 520 | + return src | ||
| 521 | + | ||
| 522 | + | ||
| 523 | + | ||
| 524 | +def clone(x, memory_format=None): | ||
| 525 | + return copy(x) | ||
| 526 | + | ||
| 527 | + | ||
| 528 | + | ||
| 529 | + aten.full.default, | ||
| 530 | + output_dtypes=DVM_SUPPORT_TYPE, | ||
| 531 | +) | ||
| 532 | +def full( | ||
| 533 | + size, | ||
| 534 | + fill_value, | ||
| 535 | + **kwargs, | ||
| 536 | +): | ||
| 537 | + size = format_shape(size) | ||
| 538 | + dtype = to_dvm_dtype(kwargs.get("dtype")) | ||
| 539 | + return f"k.full({fill_value}, {size}, {dtype})" | ||
| 540 | + | ||
| 541 | + | ||
| 542 | + | ||
| 543 | +def matmul(x, y, trans_a, trans_b): | ||
| 544 | + return f"k.matmul({x}, {y}, {trans_a}, {trans_b})" | ||
| 545 | + | ||
| 546 | + | ||
| 547 | +def matmul_bias(bias, x, y, trans_a, trans_b, beta=1, alpha=1): | ||
| 548 | + return f"k.matmul({x}, {y}, {trans_a}, {trans_b},{bias})" | ||
| 549 | + | ||
| 550 | + | ||
| 551 | + | ||
| 552 | +def addmm(z, x, y, trans_a, trans_b, use_bias, beta=1, alpha=1): | ||
| 553 | + if use_bias: | ||
| 554 | + return matmul_bias(z, x, y, trans_a, trans_b) | ||
| 555 | + if beta != 1: | ||
| 556 | + z = mul(z, beta) | ||
| 557 | + mm = matmul(x, y, trans_a, trans_b) | ||
| 558 | + if alpha != 1: | ||
| 559 | + mm = mul(mm, alpha) | ||
| 560 | + return add(mm, z) | ||
| 561 | + | ||
| 562 | + | ||
| 563 | +def load(shape, dtype): | ||
| 564 | + dtype = to_dvm_dtype(dtype) | ||
| 565 | + return f"k.load({shape}, {dtype})" | ||
| 566 | + | ||
| 567 | + | ||
| 568 | +def view_load(shape, stride, dtype): | ||
| 569 | + dtype = to_dvm_dtype(dtype) | ||
| 570 | + return f"k.view_load({shape}, {stride}, {dtype})" | ||
| 571 | + | ||
| 572 | + | ||
| 573 | +def store(x, dtype=None): | ||
| 574 | + if dtype is None: | ||
| 575 | + return f"k.store({x})" | ||
| 576 | + dtype = to_dvm_dtype(dtype) | ||
| 577 | + return f"k.store({x}, {dtype})" | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | +"""DVM load/view_load selection based on real tensor strides.""" | ||
| 2 | + | ||
| 3 | +from __future__ import annotations | ||
| 4 | + | ||
| 5 | +from typing import Sequence | ||
| 6 | + | ||
| 7 | +import torch | ||
| 8 | +from torch._inductor.virtualized import V | ||
| 9 | + | ||
| 10 | +from .op_emitter import load, view_load | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +def patch_gm_placeholder_strides_from_codegen_args( | ||
| 14 | + gm: torch.fx.GraphModule, | ||
| 15 | + arg_names: Sequence[str], | ||
| 16 | +) -> None: | ||
| 17 | + """Patch placeholder meta['val'] with Inductor buffer layout strides at codegen time.""" | ||
| 18 | + placeholders = [n for n in gm.graph.nodes if n.op == "placeholder"] | ||
| 19 | + for node, name in zip(placeholders, arg_names): | ||
| 20 | + val = node.meta.get("val") | ||
| 21 | + if isinstance(val, (torch.SymInt, torch.SymFloat)): | ||
| 22 | + continue | ||
| 23 | + if not isinstance(val, torch.Tensor): | ||
| 24 | + continue | ||
| 25 | + buf = V.graph.try_get_buffer(name) | ||
| 26 | + if buf is None: | ||
| 27 | + continue | ||
| 28 | + layout_stride = tuple(buf.get_stride()) | ||
| 29 | + if layout_stride == tuple(val.stride()): | ||
| 30 | + continue | ||
| 31 | + node.meta["val"] = torch.empty_strided( | ||
| 32 | + val.shape, | ||
| 33 | + layout_stride, | ||
| 34 | + dtype=val.dtype, | ||
| 35 | + device=val.device, | ||
| 36 | + ) | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +def codegen_maybe_view_load( | ||
| 40 | + shape: Sequence, | ||
| 41 | + stride: Sequence, | ||
| 42 | + dtype: torch.dtype, | ||
| 43 | + *, | ||
| 44 | + view_fusion_level: int, | ||
| 45 | + is_symbolic: bool, | ||
| 46 | +) -> tuple[str, bool]: | ||
| 47 | + """Return (expr, skip_cont). | ||
| 48 | + | ||
| 49 | + skip_cont=False means the caller must manually materialize a non-contiguous | ||
| 50 | + input with .contiguous() before launching the DVM kernel. | ||
| 51 | + """ | ||
| 52 | + if view_fusion_level == 0: | ||
| 53 | + return load(shape, dtype), False | ||
| 54 | + | ||
| 55 | + if view_fusion_level == 2: | ||
| 56 | + return view_load(shape, stride, dtype), True | ||
| 57 | + | ||
| 58 | + if is_symbolic: | ||
| 59 | + return load(shape, dtype), False | ||
| 60 | + | ||
| 61 | + if stride[-1] == 1 and shape[-1] != 1: | ||
| 62 | + return view_load(shape, stride, dtype), True | ||
| 63 | + | ||
| 64 | + return load(shape, dtype), False | ||
| @@ -157,6 +157,7 @@ void THNPEvent_init(PyObject *module); | |||
| 157 | void THNPGraph_init(PyObject *module); | 157 | void THNPGraph_init(PyObject *module); |
| 158 | void THNPMemPool_init(PyObject* module); | 158 | void THNPMemPool_init(PyObject* module); |
| 159 | void THNPMLIR_init(PyObject* module); | 159 | void THNPMLIR_init(PyObject* module); |
| 160 | +void THDVM_init(PyObject* module); | ||
| 160 | PyMethodDef* THNPModule_get_methods(); | 161 | PyMethodDef* THNPModule_get_methods(); |
| 161 | 162 | ||
| 162 | static std::vector<PyMethodDef> methods; | 163 | static std::vector<PyMethodDef> methods; |
| @@ -199,6 +200,7 @@ PyObject* initModule() | |||
| 199 | THNPGraph_init(module); | 200 | THNPGraph_init(module); |
| 200 | THNPMemPool_init(module); | 201 | THNPMemPool_init(module); |
| 201 | THNPMLIR_init(module); | 202 | THNPMLIR_init(module); |
| 203 | + THDVM_init(module); | ||
| 202 | 204 | ||
| 203 | RegisterNPUDeviceProperties(module); | 205 | RegisterNPUDeviceProperties(module); |
| 204 | BindGetDeviceProperties(module); | 206 | BindGetDeviceProperties(module); |
| @@ -3,9 +3,10 @@ FILE(GLOB _INDUCTOR_SRCS | |||
| 3 | aoti_runner/*.cpp | 3 | aoti_runner/*.cpp |
| 4 | aoti_torch/*.cpp | 4 | aoti_torch/*.cpp |
| 5 | aoti_torch/generated/*.cpp | 5 | aoti_torch/generated/*.cpp |
| 6 | + dvm/*.cpp | ||
| 6 | mlir/*.cpp) | 7 | mlir/*.cpp) |
| 7 | 8 | ||
| 8 | LIST(APPEND INDUCTOR_SRCS ${_INDUCTOR_SRCS}) | 9 | LIST(APPEND INDUCTOR_SRCS ${_INDUCTOR_SRCS}) |
| 9 | 10 | ||
| 10 | # Pass to parent | 11 | # Pass to parent |
| 11 | -set(INDUCTOR_SRCS ${INDUCTOR_SRCS} PARENT_SCOPE) | 12 | +set(INDUCTOR_SRCS ${INDUCTOR_SRCS} PARENT_SCOPE) |
| @@ -0,0 +1,811 @@ | |||
| 1 | +// Copyright (c) 2025 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 | +// Unless required by applicable law or agreed to in writing, software | ||
| 9 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
| 10 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 11 | +// See the License for the specific language governing permissions and | ||
| 12 | +// limitations under the License. | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +template <typename T> using shared_ptr_class_ = py::class_<T, std::shared_ptr<T> >; | ||
| 28 | + | ||
| 29 | +void TORCH_NPU_API THDVM_init(PyObject* module) | ||
| 30 | +{ | ||
| 31 | + using namespace dvm; | ||
| 32 | + auto torch_C_m = py::handle(module).cast<py::module>(); | ||
| 33 | + auto dvm_m = torch_C_m.def_submodule("dvm", "DVM bindings"); | ||
| 34 | + RegDvmPy(dvm_m); | ||
| 35 | + | ||
| 36 | + pybind11::class_<TorchKernelPy, KernelPy, std::shared_ptr<TorchKernelPy> >(dvm_m, "TorchKernel") | ||
| 37 | + .def(py::init<int, uint32_t>()) | ||
| 38 | + .def("set_kernel_info", &TorchKernelPy::SetKernelInfo, "set_kernel_info") | ||
| 39 | + .def("setup", &TorchKernelPy::Setup, "setup") | ||
| 40 | + .def("run", &TorchKernelPy::Run, "run kernel") | ||
| 41 | + .def("__call__", &TorchKernelPy::Call, "call kernel"); | ||
| 42 | + | ||
| 43 | + pybind11::class_<DynKernelPy, TorchKernelPy, std::shared_ptr<DynKernelPy> >(dvm_m, "DynKernel") | ||
| 44 | + .def(py::init<int, uint32_t>()) | ||
| 45 | + .def("scalar", &DynKernelPy::MakeScalar, "setup", py::arg("dtype") = DataTypePy(kDataTypeEnd)); | ||
| 46 | + | ||
| 47 | + pybind11::class_<GraphSplitKernelPy, TorchKernelPy, std::shared_ptr<GraphSplitKernelPy> >(dvm_m, "GraphSplitKernel") | ||
| 48 | + .def(py::init<>()); | ||
| 49 | + | ||
| 50 | + pybind11::class_<DynGraphSplitKernelPy, DynKernelPy, std::shared_ptr<DynGraphSplitKernelPy> >(dvm_m, | ||
| 51 | + "DynGraphSplitKernel") | ||
| 52 | + .def(py::init<>()); | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +namespace dvm { | ||
| 56 | +namespace { | ||
| 57 | +at::ScalarType DvmDType2TorchDtype(DType dtype) | ||
| 58 | +{ | ||
| 59 | + switch (dtype) { | ||
| 60 | + case DType::kFloat32: | ||
| 61 | + return at::ScalarType::Float; | ||
| 62 | + case DType::kFloat16: | ||
| 63 | + return at::ScalarType::Half; | ||
| 64 | + case DType::kBFloat16: | ||
| 65 | + return at::ScalarType::BFloat16; | ||
| 66 | + case DType::kInt32: | ||
| 67 | + return at::ScalarType::Int; | ||
| 68 | + case DType::kInt64: | ||
| 69 | + return at::ScalarType::Long; | ||
| 70 | + case DType::kBool: | ||
| 71 | + return at::ScalarType::Bool; | ||
| 72 | + default: | ||
| 73 | + throw std::runtime_error("Unsupported Dtype conversion"); | ||
| 74 | + } | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +bool IsCurrentStreamCapturing() | ||
| 78 | +{ | ||
| 79 | + return static_cast<int>(c10_npu::currentStreamCaptureStatusMayInitCtx()) != 0; | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +class FakeWsAllocator : public WsAllocator { | ||
| 83 | +public: | ||
| 84 | + explicit FakeWsAllocator(TorchKernelPy* kernel_py) : kernel_py_(kernel_py) {} | ||
| 85 | + | ||
| 86 | + void* Alloc(size_t size) override | ||
| 87 | + { | ||
| 88 | + kernel_py_->SetWorkspaceSize(size); | ||
| 89 | + return nullptr; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | +private: | ||
| 93 | + TorchKernelPy* kernel_py_ = nullptr; | ||
| 94 | +}; | ||
| 95 | + | ||
| 96 | +class ExternalWsAllocator : public WsAllocator { | ||
| 97 | +public: | ||
| 98 | + explicit ExternalWsAllocator(void* workspace_ptr) : workspace_ptr_(workspace_ptr) {} | ||
| 99 | + | ||
| 100 | + void* Alloc(size_t) override { return workspace_ptr_; } | ||
| 101 | + | ||
| 102 | +private: | ||
| 103 | + void* workspace_ptr_ = nullptr; | ||
| 104 | +}; | ||
| 105 | + | ||
| 106 | +inline std::pair<at::Tensor, void*> AllocWorkspaceV1(size_t size) | ||
| 107 | +{ | ||
| 108 | + if (size == 0) { | ||
| 109 | + return {at::Tensor(), nullptr}; | ||
| 110 | + } | ||
| 111 | + at::TensorOptions options = at::TensorOptions(c10::DeviceType::PrivateUse1); | ||
| 112 | + auto workspace_tensor = at::empty({static_cast<int64_t>(size)}, options.dtype(at::kByte)); | ||
| 113 | + auto workspace_ptr = const_cast<void*>(workspace_tensor.storage().data()); | ||
| 114 | + return {workspace_tensor, workspace_ptr}; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +inline std::pair<at::Tensor, void*> AllocWorkspaceV2(size_t size, aclrtStream stream) | ||
| 118 | +{ | ||
| 119 | + if (size == 0) { | ||
| 120 | + return {at::Tensor(), nullptr}; | ||
| 121 | + } | ||
| 122 | + auto workspace_tensor = at_npu::native::allocate_workspace(size, stream); | ||
| 123 | + auto workspace_ptr = const_cast<void*>(workspace_tensor.storage().data()); | ||
| 124 | + return {workspace_tensor, workspace_ptr}; | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +} // namespace | ||
| 128 | + | ||
| 129 | +TorchKernelPy::TorchKernelPy(int kernel_type, uint32_t flags) | ||
| 130 | + : ws_size_(0), kernel_type_(kernel_type), kernel_flags_(flags) | ||
| 131 | +{ | ||
| 132 | + SetDeterm(at::globalContext().deterministicAlgorithms()); | ||
| 133 | + kernel_.Reset(static_cast<KernelType>(kernel_type), flags); | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +TorchKernelPy::~TorchKernelPy() | ||
| 137 | +{ | ||
| 138 | + for (auto ref : shapes_) { | ||
| 139 | + delete ref; | ||
| 140 | + } | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +void TorchKernelPy::SetDeterm(bool enable) | ||
| 144 | +{ | ||
| 145 | + auto& conf = Config::Instance(); | ||
| 146 | + if (enable) { | ||
| 147 | + conf.SetDeterm(); | ||
| 148 | + } else { | ||
| 149 | + conf.UnsetDeterm(); | ||
| 150 | + } | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +void TorchKernelPy::SetTuning(bool enable) | ||
| 154 | +{ | ||
| 155 | + auto& conf = Config::Instance(); | ||
| 156 | + if (enable) { | ||
| 157 | + conf.SetOnlineTuner().SetLazyTuner(); | ||
| 158 | + } else { | ||
| 159 | + conf.UnsetOnlineTuner().UnsetLazyTuner(); | ||
| 160 | + } | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +IntArrayRef* TorchKernelPy::GetShapeRef(py::object shape) { return SymIntArraytoShapeRef(shape); } | ||
| 164 | + | ||
| 165 | +ShapeRef* TorchKernelPy::SymIntArraytoShapeRef(py::object shape) | ||
| 166 | +{ | ||
| 167 | + auto shape_array = shape.cast<py::sequence>(); | ||
| 168 | + auto& ref = shapes_.emplace_back(new ShapeWithRef(shape_array.size())); | ||
| 169 | + for (size_t i = 0; i < ref->size; ++i) { | ||
| 170 | + ref->shape_data[i] = shape_array[i].cast<int64_t>(); | ||
| 171 | + } | ||
| 172 | + return ref; | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +ShapeRef* TorchKernelPy::SymIntArraytoShapeRef(at::IntArrayRef shape_array) | ||
| 176 | +{ | ||
| 177 | + auto& ref = shapes_.emplace_back(new ShapeWithRef(shape_array.size())); | ||
| 178 | + for (size_t i = 0; i < ref->size; ++i) { | ||
| 179 | + ref->shape_data[i] = shape_array[i]; | ||
| 180 | + } | ||
| 181 | + return ref; | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +py::object TorchKernelPy::Load(py::object shape, DataTypePy type) | ||
| 185 | +{ | ||
| 186 | + ShapeRef* shape_ref = SymIntArraytoShapeRef(shape); | ||
| 187 | + auto op = kernel_.Load(nullptr, shape_ref, type); | ||
| 188 | + loads_.emplace_back(op); | ||
| 189 | + load_types_.emplace_back(LoadType::Load); | ||
| 190 | + return ObjToPy(op); | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +py::object TorchKernelPy::GlobalAccess(py::object shape, DataTypePy type) | ||
| 194 | +{ | ||
| 195 | + ShapeRef* shape_ref = SymIntArraytoShapeRef(shape); | ||
| 196 | + auto op = kernel_.GlobalAccess(nullptr, shape_ref, type); | ||
| 197 | + loads_.emplace_back(op); | ||
| 198 | + return ObjToPy(op); | ||
| 199 | +} | ||
| 200 | + | ||
| 201 | +py::object TorchKernelPy::ViewLoad(py::object shape, py::object stride, DataTypePy type) | ||
| 202 | +{ | ||
| 203 | + ShapeRef* shape_ref = SymIntArraytoShapeRef(shape); | ||
| 204 | + ShapeRef* stride_ref = SymIntArraytoShapeRef(stride); | ||
| 205 | + auto op = kernel_.Load(nullptr, shape_ref, stride_ref, type); | ||
| 206 | + loads_.emplace_back(op); | ||
| 207 | + load_types_.emplace_back(LoadType::View); | ||
| 208 | + return ObjToPy(op); | ||
| 209 | +} | ||
| 210 | + | ||
| 211 | +py::object TorchKernelPy::GatherLoad(py::object shape, py::object index, DataTypePy type, int axis) | ||
| 212 | +{ | ||
| 213 | + ShapeRef* shape_ref = SymIntArraytoShapeRef(shape); | ||
| 214 | + auto op = kernel_.GatherLoad(nullptr, shape_ref, PyToObj(index), axis, type); | ||
| 215 | + loads_.emplace_back(op); | ||
| 216 | + return ObjToPy(op); | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +py::object TorchKernelPy::Store(py::object obj, DataTypePy type) | ||
| 220 | +{ | ||
| 221 | + auto in_obj = PyToObj(obj); | ||
| 222 | + if (type != kDataTypeEnd) { | ||
| 223 | + in_obj = kernel_.Cast(in_obj, type); | ||
| 224 | + } | ||
| 225 | + auto op = kernel_.Store(nullptr, in_obj); | ||
| 226 | + return ObjToPy(stores_.emplace_back(op)); | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | +py::object TorchKernelPy::ViewStore(py::object obj, py::object stride, DataTypePy type) | ||
| 230 | +{ | ||
| 231 | + auto in_obj = PyToObj(obj); | ||
| 232 | + if (type != kDataTypeEnd) { | ||
| 233 | + in_obj = kernel_.Cast(in_obj, type); | ||
| 234 | + } | ||
| 235 | + auto op = kernel_.Store(nullptr, in_obj, GetShapeRef(stride)); | ||
| 236 | + return ObjToPy(stores_.emplace_back(op)); | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +void TorchKernelPy::Setup() | ||
| 240 | +{ | ||
| 241 | + SetupRelocs(); | ||
| 242 | + ws_size_ = kernel_.CodeGen(); | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +void TorchKernelPy::SetupRelocs() | ||
| 246 | +{ | ||
| 247 | + relocs_.clear(); | ||
| 248 | + relocs_.reserve(loads_.size() + stores_.size()); | ||
| 249 | + for (auto op : loads_) { | ||
| 250 | + relocs_.emplace_back(op, nullptr); | ||
| 251 | + } | ||
| 252 | + for (auto op : stores_) { | ||
| 253 | + relocs_.emplace_back(op, nullptr); | ||
| 254 | + } | ||
| 255 | +} | ||
| 256 | + | ||
| 257 | +TorchKernelPy::ParsedCallInputs TorchKernelPy::ParseTensorCallInputs(py::args inputs, | ||
| 258 | + std::vector<at::Tensor>& tensor_refs) const | ||
| 259 | +{ | ||
| 260 | + TORCH_CHECK(inputs.size() == loads_.size(), "Call expects ", loads_.size(), " input tensors, got ", inputs.size()); | ||
| 261 | + | ||
| 262 | + auto addr = std::make_shared<std::vector<void*> >(); | ||
| 263 | + addr->resize(relocs_.size()); | ||
| 264 | + tensor_refs.reserve(loads_.size()); | ||
| 265 | + at::TensorOptions options = at::TensorOptions(c10::DeviceType::PrivateUse1); | ||
| 266 | + | ||
| 267 | + for (size_t i = 0; i < loads_.size(); ++i) { | ||
| 268 | + auto tensor = inputs[i].cast<at::Tensor>(); | ||
| 269 | + if (tensor.device().type() == c10::DeviceType::CPU) { | ||
| 270 | + tensor = at_npu::native::OpPreparation::copy_tensor_host_to_device(tensor); | ||
| 271 | + } | ||
| 272 | + (*addr)[i] = tensor.data_ptr(); | ||
| 273 | + options = tensor.options(); | ||
| 274 | + tensor_refs.emplace_back(tensor); | ||
| 275 | + } | ||
| 276 | + return {addr, options}; | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +void TorchKernelPy::Run(py::args args) | ||
| 280 | +{ | ||
| 281 | + const auto num_inputs = loads_.size(); | ||
| 282 | + const auto num_outputs = stores_.size(); | ||
| 283 | + TORCH_CHECK(args.size() == num_inputs + num_outputs, "DVM kernel run expects ", num_inputs + num_outputs, | ||
| 284 | + " tensors, got ", args.size()); | ||
| 285 | + auto addr = std::make_shared<std::vector<void*> >(); | ||
| 286 | + tensor_list_.reserve(args.size()); | ||
| 287 | + out_refs_.reserve(num_outputs); | ||
| 288 | + addr->resize(num_inputs + num_outputs); | ||
| 289 | + for (size_t i = 0; i < args.size(); i++) { | ||
| 290 | + auto tensor = args[i].cast<at::Tensor>(); | ||
| 291 | + if (i < num_inputs && tensor.device().type() == c10::DeviceType::CPU) { | ||
| 292 | + tensor = at_npu::native::OpPreparation::copy_tensor_host_to_device(tensor); | ||
| 293 | + } | ||
| 294 | + if (i < num_inputs) { | ||
| 295 | + if (!tensor.is_contiguous() && load_types_[i] == LoadType::Load) { | ||
| 296 | + tensor = tensor.contiguous(); | ||
| 297 | + } | ||
| 298 | + } else if (!tensor.is_contiguous()) { | ||
| 299 | + tensor = out_refs_.emplace_back(tensor, at::empty(tensor.sizes(), tensor.options())).second; | ||
| 300 | + } | ||
| 301 | + tensor_list_.emplace_back(tensor); | ||
| 302 | + (*addr)[i] = tensor.data_ptr(); | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + aclrtStream stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 306 | + if (!IsCurrentStreamCapturing()) { | ||
| 307 | + auto dvm_call = [this, addr, stream]() { return LaunchV2(addr->data(), stream); }; | ||
| 308 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, dvm_call); | ||
| 309 | + } else { | ||
| 310 | + auto [workspace_tensor, workspace_ptr] = AllocWorkspaceV1(ws_size_); | ||
| 311 | + auto dvm_call = [this, addr, stream, workspace_ptr]() { | ||
| 312 | + return LaunchV1(addr->data(), stream, workspace_ptr); | ||
| 313 | + }; | ||
| 314 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, dvm_call); | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + for (auto& [ori_tensor, cur_tensor] : out_refs_) { | ||
| 318 | + ori_tensor.copy_(cur_tensor); | ||
| 319 | + } | ||
| 320 | + tensor_list_.clear(); | ||
| 321 | + out_refs_.clear(); | ||
| 322 | +} | ||
| 323 | + | ||
| 324 | +py::object TorchKernelPy::Call(py::args args) | ||
| 325 | +{ | ||
| 326 | + const auto num_inputs = loads_.size(); | ||
| 327 | + const auto num_outputs = stores_.size(); | ||
| 328 | + std::vector<at::Tensor> tensor_list; | ||
| 329 | + auto parsed = ParseTensorCallInputs(args, tensor_list); | ||
| 330 | + auto ret = CreateOutputs(parsed.options, parsed.addr->data() + num_inputs); | ||
| 331 | + aclrtStream stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 332 | + if (!IsCurrentStreamCapturing()) { | ||
| 333 | + auto dvm_call = [this, addr = parsed.addr, stream]() { return LaunchV2(addr->data(), stream); }; | ||
| 334 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, dvm_call); | ||
| 335 | + } else { | ||
| 336 | + auto [workspace_tensor, workspace_ptr] = AllocWorkspaceV1(ws_size_); | ||
| 337 | + auto dvm_call = [this, addr = parsed.addr, stream, workspace_ptr]() { | ||
| 338 | + return LaunchV1(addr->data(), stream, workspace_ptr); | ||
| 339 | + }; | ||
| 340 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, dvm_call); | ||
| 341 | + } | ||
| 342 | + return ret; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +py::object TorchKernelPy::CreateOutputs(const at::TensorOptions& options, void** addr) | ||
| 346 | +{ | ||
| 347 | + auto create_output = [this, options](NDObject* store) -> at::Tensor { | ||
| 348 | + auto shape_ref = kernel_.GetShape(store); | ||
| 349 | + c10::IntArrayRef shape = c10::IntArrayRef(shape_ref->data, shape_ref->size); | ||
| 350 | + at::ScalarType dtype = DvmDType2TorchDtype(kernel_.GetDType(store)); | ||
| 351 | + return at_npu::native::OpPreparation::apply_tensor_without_format(shape, options.dtype(dtype)); | ||
| 352 | + }; | ||
| 353 | + if (stores_.size() == 1) { | ||
| 354 | + at::Tensor tensor = create_output(stores_.front()); | ||
| 355 | + *addr = tensor.data_ptr(); | ||
| 356 | + return py::cast(tensor); | ||
| 357 | + } | ||
| 358 | + py::tuple tuple_ret(stores_.size()); | ||
| 359 | + for (size_t i = 0; i < stores_.size(); ++i) { | ||
| 360 | + at::Tensor tensor = create_output(stores_[i]); | ||
| 361 | + *addr = tensor.data_ptr(); | ||
| 362 | + addr++; | ||
| 363 | + tuple_ret[i] = py::cast(tensor); | ||
| 364 | + } | ||
| 365 | + return tuple_ret; | ||
| 366 | +} | ||
| 367 | + | ||
| 368 | +int TorchKernelPy::LaunchV2(void** addr, aclrtStream stream) | ||
| 369 | +{ | ||
| 370 | + auto workspace_pair = AllocWorkspaceV2(ws_size_, stream); | ||
| 371 | + auto workspace_ptr = workspace_pair.second; | ||
| 372 | + return LaunchV1(addr, stream, workspace_ptr); | ||
| 373 | +} | ||
| 374 | + | ||
| 375 | +int TorchKernelPy::LaunchV1(void** addr, aclrtStream stream, void* workspace_ptr) | ||
| 376 | +{ | ||
| 377 | + for (size_t i = 0; i < relocs_.size(); ++i) { | ||
| 378 | + relocs_[i].addr = addr[i]; | ||
| 379 | + } | ||
| 380 | + return kernel_.Launch(relocs_.data(), relocs_.size(), workspace_ptr, stream); | ||
| 381 | +} | ||
| 382 | + | ||
| 383 | +void* GraphSplitBase::Alloc(size_t size) | ||
| 384 | +{ | ||
| 385 | + auto [workspace_tensor, workspace_ptr] = AllocWorkspaceV2(size, stream_); | ||
| 386 | + ws_ = std::move(workspace_tensor); | ||
| 387 | + return workspace_ptr; | ||
| 388 | +} | ||
| 389 | + | ||
| 390 | +int GraphSplitBase::LaunchV1(Kernel& kernel, void** addr, aclrtStream stream, std::vector<RelocEntry>& relocs, | ||
| 391 | + void* workspace_ptr) | ||
| 392 | +{ | ||
| 393 | + for (size_t i = 0; i < relocs.size(); ++i) { | ||
| 394 | + relocs[i].addr = addr[i]; | ||
| 395 | + } | ||
| 396 | + ExternalWsAllocator allocator(workspace_ptr); | ||
| 397 | + kernel.CodeGen(relocs.data(), relocs.size(), &allocator); | ||
| 398 | + int ret = kernel.Launch(stream); | ||
| 399 | + return ret; | ||
| 400 | +} | ||
| 401 | + | ||
| 402 | +int GraphSplitBase::LaunchV2(Kernel& kernel, void** addr, aclrtStream stream, std::vector<RelocEntry>& relocs) | ||
| 403 | +{ | ||
| 404 | + stream_ = stream; | ||
| 405 | + for (size_t i = 0; i < relocs.size(); ++i) { | ||
| 406 | + relocs[i].addr = addr[i]; | ||
| 407 | + } | ||
| 408 | + kernel.CodeGen(relocs.data(), relocs.size(), this); | ||
| 409 | + int ret = kernel.Launch(stream); | ||
| 410 | + ws_.reset(); | ||
| 411 | + return ret; | ||
| 412 | +} | ||
| 413 | + | ||
| 414 | +void GraphSplitKernelPy::Setup() | ||
| 415 | +{ | ||
| 416 | + SetupRelocs(); | ||
| 417 | + kernel_.Infer(); | ||
| 418 | + SetWorkspaceSize(0); | ||
| 419 | + FakeWsAllocator fake_alloc(this); | ||
| 420 | + kernel_.CodeGen(nullptr, 0, &fake_alloc); | ||
| 421 | +} | ||
| 422 | + | ||
| 423 | +void GraphSplitKernelPy::Run(py::args) | ||
| 424 | +{ | ||
| 425 | + TORCH_CHECK(false, "GraphSplitKernel::Run is unsupported. Use Call/__call__ to create outputs internally."); | ||
| 426 | +} | ||
| 427 | + | ||
| 428 | +py::object GraphSplitKernelPy::Call(py::args inputs) | ||
| 429 | +{ | ||
| 430 | + std::vector<at::Tensor> tensor_list; | ||
| 431 | + auto parsed = ParseTensorCallInputs(inputs, tensor_list); | ||
| 432 | + auto ret = CreateOutputs(parsed.options, parsed.addr->data() + loads_.size()); | ||
| 433 | + auto stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 434 | + if (!IsCurrentStreamCapturing()) { | ||
| 435 | + auto launch_call = [this, stream, addr = parsed.addr]() -> int { | ||
| 436 | + return GraphSplitBase::LaunchV2(kernel_, addr->data(), stream, relocs_); | ||
| 437 | + }; | ||
| 438 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, launch_call); | ||
| 439 | + } else { | ||
| 440 | + auto [workspace_tensor, workspace_ptr] = AllocWorkspaceV1(ws_size_); | ||
| 441 | + auto launch_call = [this, stream, addr = parsed.addr, workspace_ptr]() -> int { | ||
| 442 | + return GraphSplitBase::LaunchV1(kernel_, addr->data(), stream, relocs_, workspace_ptr); | ||
| 443 | + }; | ||
| 444 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, launch_call); | ||
| 445 | + } | ||
| 446 | + return ret; | ||
| 447 | +} | ||
| 448 | + | ||
| 449 | +std::unique_ptr<DynKernelPy> DynGraphSplitKernelPy::CloneExecutor() const | ||
| 450 | +{ | ||
| 451 | + auto executor = std::make_unique<DynGraphSplitKernelPy>(); | ||
| 452 | + CloneExecutorStateTo(*executor); | ||
| 453 | + return executor; | ||
| 454 | +} | ||
| 455 | + | ||
| 456 | +void DynGraphSplitKernelPy::Setup() { DynKernelPy::Setup(); } | ||
| 457 | + | ||
| 458 | +void DynGraphSplitKernelPy::Run(py::args) | ||
| 459 | +{ | ||
| 460 | + TORCH_CHECK(false, "DynGraphSplitKernel::Run is unsupported. Use Call/__call__ to create outputs internally."); | ||
| 461 | +} | ||
| 462 | + | ||
| 463 | +py::object DynGraphSplitKernelPy::Call(py::args inputs) | ||
| 464 | +{ | ||
| 465 | + TORCH_CHECK(!IsCurrentStreamCapturing(), | ||
| 466 | + "DynGraphSplitKernel does not support stream capture: dynamic shape requires runtime Infer."); | ||
| 467 | + auto* executor = static_cast<DynGraphSplitKernelPy*>(AcquireExecutor()); | ||
| 468 | + std::vector<at::Tensor> tensor_list; | ||
| 469 | + auto parsed = executor->ParseDynCallInputs(inputs, tensor_list); | ||
| 470 | + | ||
| 471 | + executor->UpdateSymShapeData(); | ||
| 472 | + executor->kernel_.Infer(); | ||
| 473 | + auto ret = executor->CreateOutputs(parsed.options, parsed.addr->data() + executor->loads_.size()); | ||
| 474 | + auto stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 475 | + auto launch_call = [this, executor, stream, addr = parsed.addr]() -> int { | ||
| 476 | + int ret = executor->GraphSplitBase::LaunchV2(executor->kernel_, addr->data(), stream, executor->relocs_); | ||
| 477 | + ReleaseExecutor(executor); | ||
| 478 | + return ret; | ||
| 479 | + }; | ||
| 480 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, launch_call); | ||
| 481 | + return ret; | ||
| 482 | +} | ||
| 483 | + | ||
| 484 | +DynKernelPy::~DynKernelPy() | ||
| 485 | +{ | ||
| 486 | + for (auto ref : dyn_load_shapes_) { | ||
| 487 | + delete ref; | ||
| 488 | + } | ||
| 489 | +} | ||
| 490 | + | ||
| 491 | +DynKernelPy::LoadShapeRef* DynKernelPy::GetDynLoadShapeRef(size_t dim_size) | ||
| 492 | +{ | ||
| 493 | + static int64_t init_dyn_data[ShapeWithRef::MAX_SIZE] = {-1}; | ||
| 494 | + auto ref = new LoadShapeRef(); | ||
| 495 | + ref->shape.data = init_dyn_data; | ||
| 496 | + ref->shape.size = dim_size; | ||
| 497 | + ref->stride.data = nullptr; | ||
| 498 | + ref->stride.size = 0; | ||
| 499 | + dyn_load_shapes_.push_back(ref); | ||
| 500 | + return ref; | ||
| 501 | +} | ||
| 502 | + | ||
| 503 | +py::object DynKernelPy::Load(py::object shape, DataTypePy type) | ||
| 504 | +{ | ||
| 505 | + auto shape_seq = shape.cast<py::sequence>(); | ||
| 506 | + auto ref = GetDynLoadShapeRef(shape_seq.size()); | ||
| 507 | + ShapeRef* shape_ref = &ref->shape; | ||
| 508 | + auto op = kernel_.Load(nullptr, shape_ref, type); | ||
| 509 | + loads_.emplace_back(op); | ||
| 510 | + load_types_.emplace_back(LoadType::Load); | ||
| 511 | + return ObjToPy(op); | ||
| 512 | +} | ||
| 513 | + | ||
| 514 | +py::object DynKernelPy::GlobalAccess(py::object shape, DataTypePy type) | ||
| 515 | +{ | ||
| 516 | + auto shape_seq = shape.cast<py::sequence>(); | ||
| 517 | + auto ref = GetDynLoadShapeRef(shape_seq.size()); | ||
| 518 | + ShapeRef* shape_ref = &ref->shape; | ||
| 519 | + auto op = kernel_.GlobalAccess(nullptr, shape_ref, type); | ||
| 520 | + loads_.emplace_back(op); | ||
| 521 | + return ObjToPy(op); | ||
| 522 | +} | ||
| 523 | + | ||
| 524 | +py::object DynKernelPy::ViewLoad(py::object shape, py::object stride, DataTypePy type) | ||
| 525 | +{ | ||
| 526 | + auto shape_seq = shape.cast<py::sequence>(); | ||
| 527 | + auto ref = GetDynLoadShapeRef(shape_seq.size()); | ||
| 528 | + ref->stride = ref->shape; | ||
| 529 | + ShapeRef* shape_ref = &ref->shape; | ||
| 530 | + ShapeRef* stride_ref = &ref->stride; | ||
| 531 | + auto op = kernel_.Load(nullptr, shape_ref, stride_ref, type); | ||
| 532 | + loads_.emplace_back(op); | ||
| 533 | + load_types_.emplace_back(LoadType::View); | ||
| 534 | + return ObjToPy(op); | ||
| 535 | +} | ||
| 536 | + | ||
| 537 | +py::object DynKernelPy::GatherLoad(py::object shape, py::object index, DataTypePy type, int axis) | ||
| 538 | +{ | ||
| 539 | + auto shape_seq = shape.cast<py::sequence>(); | ||
| 540 | + auto ref = GetDynLoadShapeRef(shape_seq.size()); | ||
| 541 | + ShapeRef* shape_ref = &ref->shape; | ||
| 542 | + auto op = kernel_.GatherLoad(nullptr, shape_ref, PyToObj(index), axis, type); | ||
| 543 | + loads_.emplace_back(op); | ||
| 544 | + return ObjToPy(op); | ||
| 545 | +} | ||
| 546 | + | ||
| 547 | +ShapeRef* DynKernelPy::SymIntArraytoShapeRef(py::object shape) | ||
| 548 | +{ | ||
| 549 | + auto shape_array = shape.cast<py::sequence>(); | ||
| 550 | + auto& ref = shapes_.emplace_back(new ShapeWithRef(shape_array.size())); | ||
| 551 | + auto& sym_shape = sym_shape_.emplace_back(); | ||
| 552 | + for (size_t i = 0; i < ref->size; ++i) { | ||
| 553 | + ref->shape_data[i] = -1; | ||
| 554 | + if (py::isinstance<py::int_>(shape_array[i])) { | ||
| 555 | + auto sym_ptr = const_input_.emplace_back(std::make_shared<ScalarRefPy>()); | ||
| 556 | + sym_ptr->data_ = shape_array[i].cast<int64_t>(); | ||
| 557 | + sym_shape.emplace_back(sym_ptr); | ||
| 558 | + } else { | ||
| 559 | + sym_shape.emplace_back(shape_array[i].cast<ScalarRefPyPtr>()); | ||
| 560 | + } | ||
| 561 | + } | ||
| 562 | + return ref; | ||
| 563 | +} | ||
| 564 | + | ||
| 565 | +void DynKernelPy::UpdateSymShapeData() | ||
| 566 | +{ | ||
| 567 | + for (size_t i = 0; i < sym_shape_.size(); i++) { | ||
| 568 | + for (size_t j = 0; j < shapes_[i]->size; j++) { | ||
| 569 | + shapes_[i]->shape_data[j] = sym_shape_[i][j]->data_.i64; | ||
| 570 | + } | ||
| 571 | + } | ||
| 572 | +} | ||
| 573 | + | ||
| 574 | +std::unique_ptr<DynKernelPy> DynKernelPy::CloneExecutor() const | ||
| 575 | +{ | ||
| 576 | + auto executor = std::make_unique<DynKernelPy>(kernel_type_, kernel_flags_); | ||
| 577 | + CloneExecutorStateTo(*executor); | ||
| 578 | + return executor; | ||
| 579 | +} | ||
| 580 | + | ||
| 581 | +void DynKernelPy::CloneExecutorStateTo(DynKernelPy& executor) const | ||
| 582 | +{ | ||
| 583 | + SplitCloneHelper helper; | ||
| 584 | + std::unordered_map<ScalarRef*, ScalarRefPyPtr> scalar_to_owner; | ||
| 585 | + | ||
| 586 | + executor.ws_size_ = ws_size_; | ||
| 587 | + executor.op_name_ = op_name_; | ||
| 588 | + executor.op_fullname_ = op_fullname_; | ||
| 589 | + executor.load_types_ = load_types_; | ||
| 590 | + executor.kernel_.SetNameHint(executor.op_name_.c_str(), executor.op_fullname_.c_str()); | ||
| 591 | + | ||
| 592 | + executor.shapes_.reserve(shapes_.size()); | ||
| 593 | + for (auto ref : shapes_) { | ||
| 594 | + auto clone_ref = new ShapeWithRef(ref->size); | ||
| 595 | + for (size_t i = 0; i < ref->size; ++i) { | ||
| 596 | + clone_ref->shape_data[i] = ref->shape_data[i]; | ||
| 597 | + } | ||
| 598 | + executor.shapes_.push_back(clone_ref); | ||
| 599 | + helper.ref_map_[ref] = clone_ref; | ||
| 600 | + } | ||
| 601 | + | ||
| 602 | + executor.dyn_load_shapes_.reserve(dyn_load_shapes_.size()); | ||
| 603 | + for (auto ref : dyn_load_shapes_) { | ||
| 604 | + auto clone_ref = new LoadShapeRef(); | ||
| 605 | + clone_ref->shape = ref->shape; | ||
| 606 | + clone_ref->stride = ref->stride; | ||
| 607 | + executor.dyn_load_shapes_.push_back(clone_ref); | ||
| 608 | + helper.ref_map_[&ref->shape] = &clone_ref->shape; | ||
| 609 | + helper.ref_map_[&ref->stride] = &clone_ref->stride; | ||
| 610 | + } | ||
| 611 | + | ||
| 612 | + auto clone_scalar = [&helper, &scalar_to_owner](const ScalarRefPyPtr& src, std::vector<ScalarRefPyPtr>& dst) { | ||
| 613 | + auto clone = std::make_shared<ScalarRefPy>(); | ||
| 614 | + clone->data_ = src->data_; | ||
| 615 | + helper.ref_map_[&src->data_] = &clone->data_; | ||
| 616 | + scalar_to_owner[&clone->data_] = clone; | ||
| 617 | + dst.push_back(clone); | ||
| 618 | + }; | ||
| 619 | + for (const auto& scalar : const_input_) { | ||
| 620 | + clone_scalar(scalar, executor.const_input_); | ||
| 621 | + } | ||
| 622 | + for (const auto& scalar : sym_scalar_input_) { | ||
| 623 | + clone_scalar(scalar, executor.sym_scalar_input_); | ||
| 624 | + } | ||
| 625 | + | ||
| 626 | + executor.kernel_.Clone(kernel_, helper); | ||
| 627 | + executor.loads_.reserve(loads_.size()); | ||
| 628 | + for (auto op : loads_) { | ||
| 629 | + executor.loads_.push_back(helper.GetClone(op)); | ||
| 630 | + } | ||
| 631 | + executor.stores_.reserve(stores_.size()); | ||
| 632 | + for (auto op : stores_) { | ||
| 633 | + executor.stores_.push_back(helper.GetClone(op)); | ||
| 634 | + } | ||
| 635 | + | ||
| 636 | + executor.sym_shape_.reserve(sym_shape_.size()); | ||
| 637 | + for (const auto& shape_scalars : sym_shape_) { | ||
| 638 | + auto& clone_shape_scalars = executor.sym_shape_.emplace_back(); | ||
| 639 | + clone_shape_scalars.reserve(shape_scalars.size()); | ||
| 640 | + for (const auto& scalar : shape_scalars) { | ||
| 641 | + auto it = helper.ref_map_.find(&scalar->data_); | ||
| 642 | + TORCH_CHECK(it != helper.ref_map_.end(), "Failed to clone symbolic shape scalar reference."); | ||
| 643 | + auto clone_scalar_ref = static_cast<ScalarRef*>(it->second); | ||
| 644 | + auto owner_it = scalar_to_owner.find(clone_scalar_ref); | ||
| 645 | + TORCH_CHECK(owner_it != scalar_to_owner.end(), "Failed to remap cloned symbolic shape scalar."); | ||
| 646 | + clone_shape_scalars.push_back(owner_it->second); | ||
| 647 | + } | ||
| 648 | + } | ||
| 649 | + | ||
| 650 | + executor.SetupRelocs(); | ||
| 651 | +} | ||
| 652 | + | ||
| 653 | +TorchKernelPy::ParsedCallInputs DynKernelPy::ParseDynCallInputs(py::args inputs, | ||
| 654 | + std::vector<at::Tensor>& tensor_refs) const | ||
| 655 | +{ | ||
| 656 | + TORCH_CHECK(inputs.size() == dyn_load_shapes_.size() + sym_scalar_input_.size(), "Dynamic call expects ", | ||
| 657 | + dyn_load_shapes_.size() + sym_scalar_input_.size(), " args, got ", inputs.size()); | ||
| 658 | + | ||
| 659 | + auto addr = std::make_shared<std::vector<void*> >(); | ||
| 660 | + addr->resize(relocs_.size()); | ||
| 661 | + tensor_refs.reserve(dyn_load_shapes_.size()); | ||
| 662 | + at::TensorOptions options = at::TensorOptions(c10::DeviceType::PrivateUse1); | ||
| 663 | + | ||
| 664 | + size_t input_index = 0; | ||
| 665 | + size_t sym_scalar_index = 0; | ||
| 666 | + for (size_t i = 0; i < inputs.size(); ++i) { | ||
| 667 | + if (THPVariable_Check(inputs[i].ptr())) { | ||
| 668 | + auto tensor = inputs[i].cast<at::Tensor>(); | ||
| 669 | + if (tensor.device().type() == c10::DeviceType::CPU) { | ||
| 670 | + tensor = at_npu::native::OpPreparation::copy_tensor_host_to_device(tensor); | ||
| 671 | + } | ||
| 672 | + (*addr)[input_index] = tensor.data_ptr(); | ||
| 673 | + auto ref = dyn_load_shapes_[input_index++]; | ||
| 674 | + if (ref->stride.data) { | ||
| 675 | + ref->stride.data = tensor.strides().data(); | ||
| 676 | + } | ||
| 677 | + ref->shape.data = tensor.sizes().data(); | ||
| 678 | + options = tensor.options(); | ||
| 679 | + tensor_refs.emplace_back(tensor); | ||
| 680 | + } else if (py::isinstance<c10::SymFloat>(inputs[i])) { | ||
| 681 | + sym_scalar_input_[sym_scalar_index++]->data_ = | ||
| 682 | + static_cast<float>(inputs[i].cast<c10::SymFloat>().expect_float()); | ||
| 683 | + } else if (py::isinstance<c10::SymInt>(inputs[i])) { | ||
| 684 | + sym_scalar_input_[sym_scalar_index++]->data_ = inputs[i].cast<c10::SymInt>().expect_int(); | ||
| 685 | + } else if (py::isinstance<py::float_>(inputs[i])) { | ||
| 686 | + sym_scalar_input_[sym_scalar_index++]->data_ = inputs[i].cast<float>(); | ||
| 687 | + } else if (py::isinstance<py::int_>(inputs[i])) { | ||
| 688 | + sym_scalar_input_[sym_scalar_index++]->data_ = inputs[i].cast<int64_t>(); | ||
| 689 | + } else { | ||
| 690 | + const char* py_type = inputs[i].ptr() ? Py_TYPE(inputs[i].ptr())->tp_name : "<null>"; | ||
| 691 | + TORCH_CHECK( | ||
| 692 | + false, "Unsupported dynamic input type at arg[", i, | ||
| 693 | + "]. Expected one of: Tensor, c10::SymFloat, c10::SymInt, float, int. Got Python type: ", py_type); | ||
| 694 | + } | ||
| 695 | + } | ||
| 696 | + | ||
| 697 | + TORCH_CHECK(input_index == dyn_load_shapes_.size(), "Dynamic call expects ", dyn_load_shapes_.size(), | ||
| 698 | + " tensor inputs, got ", input_index); | ||
| 699 | + TORCH_CHECK(sym_scalar_index == sym_scalar_input_.size(), "Dynamic call expects ", sym_scalar_input_.size(), | ||
| 700 | + " scalar inputs, got ", sym_scalar_index); | ||
| 701 | + return {addr, options}; | ||
| 702 | +} | ||
| 703 | + | ||
| 704 | +void DynKernelPy::Setup() | ||
| 705 | +{ | ||
| 706 | + SetupRelocs(); | ||
| 707 | + dyn_executors_.push_back(this); | ||
| 708 | +} | ||
| 709 | + | ||
| 710 | +void DynKernelPy::Run(py::args args) | ||
| 711 | +{ | ||
| 712 | + TORCH_CHECK(!IsCurrentStreamCapturing(), | ||
| 713 | + "DynKernel does not support stream capture: dynamic shape requires runtime CodeGen."); | ||
| 714 | + const auto num_inputs = loads_.size(); | ||
| 715 | + const auto num_outputs = stores_.size(); | ||
| 716 | + const auto num_sym = sym_scalar_input_.size(); | ||
| 717 | + TORCH_CHECK(args.size() == num_inputs + num_outputs + num_sym, "DynKernel expects ", | ||
| 718 | + num_inputs + num_outputs + num_sym, " args, got ", args.size()); | ||
| 719 | + auto info = std::make_shared<DynamicInfo>(); | ||
| 720 | + info->shape.reserve(num_inputs); | ||
| 721 | + info->strides.reserve(num_inputs); | ||
| 722 | + info->scalars.reserve(num_sym); | ||
| 723 | + info->addr.reserve(num_inputs + num_outputs); | ||
| 724 | + tensor_list_.reserve(num_inputs + num_outputs); | ||
| 725 | + out_refs_.reserve(num_outputs); | ||
| 726 | + for (size_t i = 0; i < args.size(); i++) { | ||
| 727 | + if (THPVariable_Check(args[i].ptr())) { | ||
| 728 | + auto tensor = args[i].cast<at::Tensor>(); | ||
| 729 | + if (i < num_inputs + num_sym && tensor.device().type() == c10::DeviceType::CPU) { | ||
| 730 | + tensor = at_npu::native::OpPreparation::copy_tensor_host_to_device(tensor); | ||
| 731 | + } | ||
| 732 | + if (i < num_inputs + num_sym) { | ||
| 733 | + if (!tensor.is_contiguous() && load_types_[i] == LoadType::Load) { | ||
| 734 | + tensor = tensor.contiguous(); | ||
| 735 | + } | ||
| 736 | + } else if (!tensor.is_contiguous()) { | ||
| 737 | + tensor = out_refs_.emplace_back(tensor, at::empty(tensor.sizes(), tensor.options())).second; | ||
| 738 | + } | ||
| 739 | + if (i < num_inputs + num_sym) { | ||
| 740 | + info->shape.emplace_back(tensor.sizes().vec()); | ||
| 741 | + info->strides.emplace_back(tensor.strides().vec()); | ||
| 742 | + } | ||
| 743 | + tensor_list_.emplace_back(tensor); | ||
| 744 | + info->addr.emplace_back(tensor.data_ptr()); | ||
| 745 | + } else if (py::isinstance<c10::SymFloat>(args[i])) { | ||
| 746 | + info->scalars.emplace_back(static_cast<float>(args[i].cast<c10::SymFloat>().expect_float())); | ||
| 747 | + } else if (py::isinstance<c10::SymInt>(args[i])) { | ||
| 748 | + info->scalars.emplace_back(args[i].cast<c10::SymInt>().expect_int()); | ||
| 749 | + } else if (py::isinstance<py::float_>(args[i])) { | ||
| 750 | + info->scalars.emplace_back(args[i].cast<float>()); | ||
| 751 | + } else if (py::isinstance<py::int_>(args[i])) { | ||
| 752 | + info->scalars.emplace_back(args[i].cast<int64_t>()); | ||
| 753 | + } else { | ||
| 754 | + TORCH_CHECK(false, "Unsupported dynamic input type for DynKernel."); | ||
| 755 | + } | ||
| 756 | + } | ||
| 757 | + | ||
| 758 | + aclrtStream stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 759 | + | ||
| 760 | + auto dvm_call = [this, info, stream]() { | ||
| 761 | + for (size_t i = 0; i < loads_.size(); i++) { | ||
| 762 | + auto& ref = dyn_load_shapes_[i]; | ||
| 763 | + if (ref->stride.data) { | ||
| 764 | + ref->stride.data = info->strides[i].data(); | ||
| 765 | + } | ||
| 766 | + ref->shape.data = info->shape[i].data(); | ||
| 767 | + } | ||
| 768 | + for (size_t i = 0; i < sym_scalar_input_.size(); i++) { | ||
| 769 | + if (info->scalars[i].type == kInt64) { | ||
| 770 | + sym_scalar_input_[i]->data_ = info->scalars[i].i64; | ||
| 771 | + } else { | ||
| 772 | + sym_scalar_input_[i]->data_ = info->scalars[i].f32; | ||
| 773 | + } | ||
| 774 | + } | ||
| 775 | + UpdateSymShapeData(); | ||
| 776 | + ws_size_ = kernel_.CodeGen(); | ||
| 777 | + return LaunchV2(info->addr.data(), stream); | ||
| 778 | + }; | ||
| 779 | + | ||
| 780 | + at_npu::native::OpCommand::RunOpApiV2(op_name_, dvm_call); | ||
| 781 | + | ||
| 782 | + for (auto& [ori_tensor, cur_tensor] : out_refs_) { | ||
| 783 | + ori_tensor.copy_(cur_tensor); | ||
| 784 | + } | ||
| 785 | + tensor_list_.clear(); | ||
| 786 | + out_refs_.clear(); | ||
| 787 | +} | ||
| 788 | + | ||
| 789 | +py::object DynKernelPy::Call(py::args args) | ||
| 790 | +{ | ||
| 791 | + TORCH_CHECK(!IsCurrentStreamCapturing(), | ||
| 792 | + "DynKernel does not support stream capture: dynamic shape requires runtime CodeGen."); | ||
| 793 | + auto* executor = AcquireExecutor(); | ||
| 794 | + const auto num_inputs = executor->loads_.size(); | ||
| 795 | + std::vector<at::Tensor> tensor_list; | ||
| 796 | + auto parsed = executor->ParseDynCallInputs(args, tensor_list); | ||
| 797 | + | ||
| 798 | + executor->UpdateSymShapeData(); | ||
| 799 | + executor->ws_size_ = executor->kernel_.CodeGen(); | ||
| 800 | + auto ret = executor->CreateOutputs(parsed.options, parsed.addr->data() + num_inputs); | ||
| 801 | + aclrtStream stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 802 | + auto dvm_call = [this, executor, addr = parsed.addr, stream]() { | ||
| 803 | + int ret = executor->LaunchV2(addr->data(), stream); | ||
| 804 | + ReleaseExecutor(executor); | ||
| 805 | + return ret; | ||
| 806 | + }; | ||
| 807 | + at_npu::native::OpCommand::RunOpApiV2(executor->op_name_, dvm_call); | ||
| 808 | + return ret; | ||
| 809 | +} | ||
| 810 | +} // namespace dvm | ||
| 811 | + | ||
| @@ -0,0 +1,236 @@ | |||
| 1 | +// Copyright (c) 2025 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 | +// Unless required by applicable law or agreed to in writing, software | ||
| 9 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
| 10 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 11 | +// See the License for the specific language governing permissions and | ||
| 12 | +// limitations under the License. | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +namespace dvm { | ||
| 35 | +struct SplitCloneHelper : public dvm::CloneHelper { | ||
| 36 | + IntArrayRef* GetClone(IntArrayRef* shape) override | ||
| 37 | + { | ||
| 38 | + auto it = ref_map_.find(shape); | ||
| 39 | + return it != ref_map_.end() ? static_cast<IntArrayRef*>(it->second) : shape; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + ScalarRef* GetClone(ScalarRef* scalar) override | ||
| 43 | + { | ||
| 44 | + auto it = ref_map_.find(scalar); | ||
| 45 | + return it != ref_map_.end() ? static_cast<ScalarRef*>(it->second) : scalar; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + NDObject* GetClone(NDObject* op) override | ||
| 49 | + { | ||
| 50 | + auto it = op_map_.find(op); | ||
| 51 | + return it != op_map_.end() ? it->second : nullptr; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + void SetClone(NDObject* op, NDObject* clone) override { op_map_[op] = clone; } | ||
| 55 | + | ||
| 56 | + std::unordered_map<NDObject*, NDObject*> op_map_; | ||
| 57 | + std::unordered_map<void*, void*> ref_map_; | ||
| 58 | +}; | ||
| 59 | +class TorchKernelPy : public KernelPy { | ||
| 60 | +public: | ||
| 61 | + TorchKernelPy(int kernel_type, uint32_t flags); | ||
| 62 | + ~TorchKernelPy(); | ||
| 63 | + | ||
| 64 | + py::object Load(py::object shape, DataTypePy type) override; | ||
| 65 | + py::object GlobalAccess(py::object shape, DataTypePy type) override; | ||
| 66 | + py::object ViewLoad(py::object shape, py::object stride, DataTypePy type) override; | ||
| 67 | + py::object GatherLoad(py::object shape, py::object index, DataTypePy type, int axis) override; | ||
| 68 | + py::object Store(py::object obj, DataTypePy type) override; | ||
| 69 | + py::object ViewStore(py::object obj, py::object stride, DataTypePy type) override; | ||
| 70 | + IntArrayRef* GetShapeRef(py::object shape) override; | ||
| 71 | + | ||
| 72 | + void SetKernelInfo(const std::string& op_name, const std::string& op_fullname) | ||
| 73 | + { | ||
| 74 | + op_name_ = op_name; | ||
| 75 | + op_fullname_ = op_fullname; | ||
| 76 | + kernel_.SetNameHint(op_name_.c_str(), op_fullname_.c_str()); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + int LaunchV1(void** addr, aclrtStream stream, void* workspace_ptr); | ||
| 80 | + int LaunchV2(void** addr, aclrtStream stream); | ||
| 81 | + virtual void Setup(); | ||
| 82 | + virtual void Run(py::args args); | ||
| 83 | + virtual py::object Call(py::args args); | ||
| 84 | + | ||
| 85 | + static void SetDeterm(bool enable); | ||
| 86 | + static void SetTuning(bool enable); | ||
| 87 | + virtual ShapeRef* SymIntArraytoShapeRef(py::object shape); | ||
| 88 | + ShapeRef* SymIntArraytoShapeRef(at::IntArrayRef shape_array); | ||
| 89 | + py::object CreateOutputs(const at::TensorOptions& options, void** addr); | ||
| 90 | + void SetupRelocs(); | ||
| 91 | + void SetWorkspaceSize(size_t size) { ws_size_ = size; } | ||
| 92 | + | ||
| 93 | + struct ShapeWithRef : public ShapeRef { | ||
| 94 | + enum { MAX_SIZE = 8 }; | ||
| 95 | + ShapeWithRef(size_t sz) | ||
| 96 | + { | ||
| 97 | + data = shape_data; | ||
| 98 | + size = sz; | ||
| 99 | + } | ||
| 100 | + int64_t shape_data[MAX_SIZE]; | ||
| 101 | + }; | ||
| 102 | + | ||
| 103 | +protected: | ||
| 104 | + enum class LoadType { | ||
| 105 | + Load, | ||
| 106 | + View, | ||
| 107 | + SymBol, | ||
| 108 | + }; | ||
| 109 | + | ||
| 110 | + struct ParsedCallInputs { | ||
| 111 | + std::shared_ptr<std::vector<void*> > addr; | ||
| 112 | + at::TensorOptions options; | ||
| 113 | + }; | ||
| 114 | + | ||
| 115 | + ParsedCallInputs ParseTensorCallInputs(py::args inputs, std::vector<at::Tensor>& tensor_refs) const; | ||
| 116 | + | ||
| 117 | + std::vector<RelocEntry> relocs_; | ||
| 118 | + | ||
| 119 | + std::vector<ShapeWithRef*> shapes_; | ||
| 120 | + std::vector<NDObject*> loads_; | ||
| 121 | + std::vector<NDObject*> stores_; | ||
| 122 | + std::vector<LoadType> load_types_; | ||
| 123 | + std::vector<at::Tensor> tensor_list_; | ||
| 124 | + std::vector<std::pair<at::Tensor, at::Tensor> > out_refs_; | ||
| 125 | + size_t ws_size_; | ||
| 126 | + int kernel_type_; | ||
| 127 | + uint32_t kernel_flags_; | ||
| 128 | + std::string op_name_; | ||
| 129 | + std::string op_fullname_; | ||
| 130 | +}; | ||
| 131 | + | ||
| 132 | +class GraphSplitBase : public WsAllocator { | ||
| 133 | +public: | ||
| 134 | + virtual ~GraphSplitBase() = default; | ||
| 135 | + int LaunchV1(Kernel& kernel, void** addr, aclrtStream stream, std::vector<RelocEntry>& relocs, | ||
| 136 | + void* workspace_ptr); | ||
| 137 | + int LaunchV2(Kernel& kernel, void** addr, aclrtStream stream, std::vector<RelocEntry>& relocs); | ||
| 138 | + | ||
| 139 | + void* Alloc(size_t size) override; | ||
| 140 | + | ||
| 141 | +protected: | ||
| 142 | + at::Tensor ws_; | ||
| 143 | + aclrtStream stream_; | ||
| 144 | +}; | ||
| 145 | +class DynKernelPy : public TorchKernelPy { | ||
| 146 | +public: | ||
| 147 | + DynKernelPy(int kernel_type, uint32_t flags) : TorchKernelPy(kernel_type, flags) {} | ||
| 148 | + ~DynKernelPy(); | ||
| 149 | + py::object Load(py::object shape, DataTypePy type) override; | ||
| 150 | + py::object GlobalAccess(py::object shape, DataTypePy type) override; | ||
| 151 | + py::object ViewLoad(py::object shape, py::object stride, DataTypePy type) override; | ||
| 152 | + py::object GatherLoad(py::object shape, py::object index, DataTypePy type, int axis) override; | ||
| 153 | + struct LoadShapeRef { | ||
| 154 | + ShapeRef shape; | ||
| 155 | + ShapeRef stride; | ||
| 156 | + }; | ||
| 157 | + LoadShapeRef* GetDynLoadShapeRef(size_t dim_size); | ||
| 158 | + void Setup() override; | ||
| 159 | + void Run(py::args args) override; | ||
| 160 | + py::object Call(py::args args) override; | ||
| 161 | + ShapeRef* SymIntArraytoShapeRef(py::object shape) override; | ||
| 162 | + void UpdateSymShapeData(); | ||
| 163 | + | ||
| 164 | + py::object MakeScalar(DataTypePy type = DataTypePy(kDataTypeEnd)) | ||
| 165 | + { | ||
| 166 | + auto scalar = std::make_shared<ScalarRefPy>(type); | ||
| 167 | + sym_scalar_input_.emplace_back(scalar); | ||
| 168 | + load_types_.emplace_back(LoadType::SymBol); | ||
| 169 | + return py::cast(scalar); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + struct DynamicInfo { | ||
| 173 | + std::vector<void*> addr; | ||
| 174 | + std::vector<ScalarRef> scalars; | ||
| 175 | + std::vector<std::vector<int64_t> > shape; | ||
| 176 | + std::vector<std::vector<int64_t> > strides; | ||
| 177 | + }; | ||
| 178 | + | ||
| 179 | +protected: | ||
| 180 | + ParsedCallInputs ParseDynCallInputs(py::args inputs, std::vector<at::Tensor>& tensor_refs) const; | ||
| 181 | + DynKernelPy* AcquireExecutor() | ||
| 182 | + { | ||
| 183 | + std::lock_guard<std::mutex> lock(dyn_executor_mutex_); | ||
| 184 | + if (!dyn_executors_.empty()) { | ||
| 185 | + auto* executor = dyn_executors_.back(); | ||
| 186 | + dyn_executors_.pop_back(); | ||
| 187 | + return executor; | ||
| 188 | + } | ||
| 189 | + auto new_executor = CloneExecutor(); | ||
| 190 | + auto* executor = new_executor.get(); | ||
| 191 | + dyn_owned_executors_.push_back(std::move(new_executor)); | ||
| 192 | + return executor; | ||
| 193 | + } | ||
| 194 | + void ReleaseExecutor(DynKernelPy* executor) | ||
| 195 | + { | ||
| 196 | + std::lock_guard<std::mutex> lock(dyn_executor_mutex_); | ||
| 197 | + dyn_executors_.push_back(executor); | ||
| 198 | + } | ||
| 199 | + virtual std::unique_ptr<DynKernelPy> CloneExecutor() const; | ||
| 200 | + void CloneExecutorStateTo(DynKernelPy& executor) const; | ||
| 201 | + | ||
| 202 | + std::vector<LoadShapeRef*> dyn_load_shapes_; | ||
| 203 | + std::vector<ScalarRefPyPtr> sym_scalar_input_; | ||
| 204 | + std::vector<ScalarRefPyPtr> const_input_; | ||
| 205 | + std::vector<std::vector<ScalarRefPyPtr> > sym_shape_; | ||
| 206 | + | ||
| 207 | +private: | ||
| 208 | + std::mutex dyn_executor_mutex_; | ||
| 209 | + std::vector<DynKernelPy*> dyn_executors_; | ||
| 210 | + std::vector<std::unique_ptr<DynKernelPy>> dyn_owned_executors_; | ||
| 211 | +}; | ||
| 212 | + | ||
| 213 | +class GraphSplitKernelPy : public TorchKernelPy, public GraphSplitBase { | ||
| 214 | +public: | ||
| 215 | + GraphSplitKernelPy() : TorchKernelPy(KernelPy::K_SPLIT, KernelPy::F_UWS) {} | ||
| 216 | + void Setup() override; | ||
| 217 | + void Run(py::args inputs) override; | ||
| 218 | + py::object Call(py::args inputs) override; | ||
| 219 | +}; | ||
| 220 | + | ||
| 221 | +class DynGraphSplitKernelPy : public DynKernelPy, public GraphSplitBase { | ||
| 222 | +public: | ||
| 223 | + DynGraphSplitKernelPy() | ||
| 224 | + : DynKernelPy(KernelPy::K_SPLIT, KernelPy::F_UWS | KernelPy::F_DYN) | ||
| 225 | + { | ||
| 226 | + } | ||
| 227 | + void Setup() override; | ||
| 228 | + void Run(py::args inputs) override; | ||
| 229 | + py::object Call(py::args inputs) override; | ||
| 230 | + | ||
| 231 | +private: | ||
| 232 | + std::unique_ptr<DynKernelPy> CloneExecutor() const override; | ||
| 233 | +}; | ||
| 234 | +} // namespace dvm | ||
| 235 | + | ||
| 236 | + | ||
| @@ -8,8 +8,8 @@ | |||
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | -#include "third_party/acl/inc/experiment/msprof/toolchain/prof_api.h" | 11 | +#include "third_party/acl/inc/profiling/prof_api.h" |
| 12 | -#include "third_party/acl/inc/experiment/msprof/toolchain/prof_common.h" | 12 | +#include "third_party/acl/inc/profiling/prof_common.h" |
| 13 | 13 | ||
| 14 | struct TilingMem { | 14 | struct TilingMem { |
| 15 | std::unique_ptr<void, decltype(&aclrtFreeHost)> arg_tiling_host; | 15 | std::unique_ptr<void, decltype(&aclrtFreeHost)> arg_tiling_host; |
| @@ -167,4 +167,4 @@ void TORCH_NPU_API opcommand_call(const char* name, std::function<int()> launch_ | |||
| 167 | at_npu::native::OpCommand cmd; | 167 | at_npu::native::OpCommand cmd; |
| 168 | cmd.Name(name).SetCustomHandler(launch_call).Run(); | 168 | cmd.Name(name).SetCustomHandler(launch_call).Run(); |
| 169 | } | 169 | } |
| 170 | -#endif // BUILD_LIBTORCH | 170 | +#endif // BUILD_LIBTORCH |
| @@ -228,12 +228,12 @@ def patch_inductor_wrapper(): | |||
| 228 | else: | 228 | else: |
| 229 | src_init(self, mode, options, dynamic) | 229 | src_init(self, mode, options, dynamic) |
| 230 | backend = _resolve_npu_backend_from_wrapper(self) | 230 | backend = _resolve_npu_backend_from_wrapper(self) |
| 231 | - if backend=="mlir": | 231 | + if backend == "mlir": |
| 232 | with _NpuBackendScope(backend): | 232 | with _NpuBackendScope(backend): |
| 233 | log.info("Running MLIR backend") | 233 | log.info("Running MLIR backend") |
| 234 | device_id = torch_npu.npu.current_device() | 234 | device_id = torch_npu.npu.current_device() |
| 235 | torch_npu._C._recovery_all_npu_stream(device_id) | 235 | torch_npu._C._recovery_all_npu_stream(device_id) |
| 236 | - if backend=="dvm": | 236 | + if backend == "dvm": |
| 237 | with _NpuBackendScope(backend): | 237 | with _NpuBackendScope(backend): |
| 238 | log.info("Running dvm backend") | 238 | log.info("Running dvm backend") |
| 239 | 239 | ||