| @@ -1,58 +0,0 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
| # | |||
| # Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, | |||
| # software distributed under the License is distributed on an | |||
| # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied | |||
| import json | |||
| import os | |||
| import tempfile | |||
| import pytest | |||
| from megengine import Parameter, tensor | |||
| from megengine.core import option | |||
| from megengine.module import Module | |||
| from megengine.utils.profiler import Profiler, scope | |||
| class Simple(Module): | |||
| def __init__(self): | |||
| super().__init__() | |||
| self.a = Parameter([1.23], dtype="float32") | |||
| def forward(self, x): | |||
| x = x * self.a | |||
| return x | |||
| def test_profiler(): | |||
| tempdir = tempfile.NamedTemporaryFile() | |||
| profile_prefix = tempdir.name | |||
| profile_format = "chrome_timeline.json" | |||
| profile_path = os.path.join( | |||
| profile_prefix, "{}.{}".format(os.getpid(), profile_format) | |||
| ) | |||
| with option("enable_host_compute", 0): | |||
| with Profiler(profile_prefix, format=profile_format): | |||
| with scope("my_scope"): | |||
| oup = Simple()(tensor([1.23], dtype="float32")) | |||
| with open(profile_path, "r") as f: | |||
| events = json.load(f) | |||
| prev_ts = {} | |||
| scope_count = 0 | |||
| for event in events: | |||
| if "dur" in event: | |||
| assert event["dur"] >= 0 | |||
| elif "ts" in event and "tid" in event: | |||
| ts = event["ts"] | |||
| tid = event["tid"] | |||
| if ts == 0: | |||
| continue | |||
| assert (tid not in prev_ts) or prev_ts[tid] <= ts | |||
| prev_ts[tid] = ts | |||
| if "name" in event and event["name"] == "my_scope": | |||
| scope_count += 1 | |||
| assert scope_count > 0 and scope_count % 2 == 0 | |||
| @@ -0,0 +1,108 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
| # | |||
| # Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, | |||
| # software distributed under the License is distributed on an | |||
| # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied | |||
| import json | |||
| import os | |||
| import tempfile | |||
| import pytest | |||
| from megengine import Parameter | |||
| from megengine import distributed as dist | |||
| from megengine import tensor | |||
| from megengine.core import option | |||
| from megengine.jit import trace | |||
| from megengine.module import Module | |||
| from megengine.utils.profiler import Profiler, scope | |||
| class Simple(Module): | |||
| def __init__(self): | |||
| super().__init__() | |||
| self.a = Parameter([1.23], dtype="float32") | |||
| def forward(self, x): | |||
| x = x * self.a | |||
| return x | |||
| @pytest.mark.parametrize("format", ["chrome_timeline.json", "memory_flow.svg"]) | |||
| @pytest.mark.parametrize( | |||
| "trace_mode", [True, False, None], ids=["symbolic", "no-symbolic", "no-trace"] | |||
| ) | |||
| @pytest.mark.require_ngpu(1) | |||
| def test_profiler(format, trace_mode): | |||
| tempdir = tempfile.TemporaryDirectory() | |||
| profile_prefix = tempdir.name | |||
| profile_path = os.path.join(profile_prefix, "{}.{}".format(os.getpid(), format)) | |||
| def infer(): | |||
| with scope("my_scope"): | |||
| oup = Simple()(tensor([1.23], dtype="float32")) | |||
| return oup | |||
| if trace_mode: | |||
| infer = trace(symbolic=trace_mode)(infer) | |||
| with Profiler(profile_prefix, format=format): | |||
| infer() | |||
| print(profile_path) | |||
| assert os.path.exists(profile_path), "profiling results not found" | |||
| if format == "chrome_timeline.json": | |||
| with open(profile_path, "r") as f: | |||
| events = json.load(f) | |||
| if isinstance(events, dict): | |||
| assert "traceEvents" in events | |||
| events = events["traceEvents"] | |||
| prev_ts = {} | |||
| scope_count = 0 | |||
| for event in events: | |||
| if "dur" in event: | |||
| assert event["dur"] >= 0 | |||
| elif "ts" in event and "tid" in event: | |||
| ts = event["ts"] | |||
| tid = event["tid"] | |||
| if ts != 0: | |||
| assert (tid not in prev_ts) or prev_ts[tid] <= ts | |||
| prev_ts[tid] = ts | |||
| if "name" in event and event["name"] == "my_scope": | |||
| scope_count += 1 | |||
| assert scope_count > 0 and scope_count % 2 == 0 | |||
| @pytest.mark.parametrize("format", ["chrome_timeline.json", "memory_flow.svg"]) | |||
| @pytest.mark.parametrize( | |||
| "trace_mode", [True, False, None], ids=["symbolic", "no-symbolic", "no-trace"] | |||
| ) | |||
| @pytest.mark.isolated_distributed | |||
| @pytest.mark.require_ngpu(2) | |||
| def test_profiler_dist(format, trace_mode): | |||
| n_gpus = 2 | |||
| tempdir = tempfile.TemporaryDirectory() | |||
| profile_prefix = tempdir.name | |||
| profile_path = os.path.join(profile_prefix, "{}.{}".format(os.getpid(), format)) | |||
| def infer(): | |||
| with scope("my_scope"): | |||
| oup = Simple()(tensor([1.23], dtype="float32")) | |||
| return oup | |||
| if trace_mode: | |||
| infer = trace(symbolic=trace_mode)(infer) | |||
| @dist.launcher(n_gpus=2) | |||
| def worker(): | |||
| infer() | |||
| with Profiler(profile_prefix, format=format): | |||
| worker() | |||
| assert os.path.exists(profile_path), "profiling results not found" | |||
| assert len(os.listdir(tempdir.name)) == n_gpus + 1 | |||
| @@ -60,7 +60,7 @@ namespace mgb { | |||
| * USAGE | |||
| * | |||
| * header: | |||
| * namespace mgb { bool imperative_log_profile(const char* message); } | |||
| * namespace mgb { void imperative_log_profile(const char* message); } | |||
| * | |||
| * code: | |||
| * mgb::imperative_log_profile("MY MESSAGE"); | |||
| @@ -0,0 +1,35 @@ | |||
| /** | |||
| * \file imperative/src/test/profiler.cpp | |||
| * MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
| * | |||
| * Copyright (c) 2014-2021 Megvii Inc. All rights reserved. | |||
| * | |||
| * Unless required by applicable law or agreed to in writing, | |||
| * software distributed under the License is distributed on an | |||
| * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| */ | |||
| #include "./helper.h" | |||
| #include "megbrain/imperative/profiler.h" | |||
| #include "../impl/profiler/events.h" | |||
| using namespace mgb; | |||
| using namespace cg; | |||
| using namespace imperative; | |||
| namespace mgb { void imperative_log_profile(const char* message); } | |||
| TEST(TestProfiler, ImperativeLogProfile) { | |||
| imperative::Profiler::start_profile(); | |||
| imperative_log_profile("XXX"); | |||
| auto results = imperative::Profiler::collect(); | |||
| imperative::Profiler::stop_profile(); | |||
| mgb_assert(results.size() == 2); | |||
| auto* event_start = std::any_cast<profiler::CustomEvent>(&results[0].second.data); | |||
| auto* event_finish = std::any_cast<profiler::CustomFinishEvent>(&results[1].second.data); | |||
| mgb_assert(event_start && event_start->title == "XXX"); | |||
| mgb_assert(event_finish && event_finish->title == "XXX"); | |||
| mgb_assert(results[0].second.time < results[1].second.time); | |||
| mgb_assert(results[0].second.id < results[1].second.id); | |||
| } | |||