@@ -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 | * USAGE | ||||
* | * | ||||
* header: | * header: | ||||
* namespace mgb { bool imperative_log_profile(const char* message); } | |||||
* namespace mgb { void imperative_log_profile(const char* message); } | |||||
* | * | ||||
* code: | * code: | ||||
* mgb::imperative_log_profile("MY MESSAGE"); | * 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); | |||||
} |