diff --git a/imperative/python/megengine/traced_module/traced_module.py b/imperative/python/megengine/traced_module/traced_module.py index dbce38f8f4f166d41ba016ba837a9215a47b5fa4..0f156d18b509ebaf6f81b10e55f1bd1a75cfb2bb 100644 --- a/imperative/python/megengine/traced_module/traced_module.py +++ b/imperative/python/megengine/traced_module/traced_module.py @@ -290,6 +290,7 @@ def _wrap_mnode_call(orig_call): active_module_tracer().current_scope()._add_input(self) rst = obj(*args, **kwargs) else: + raise TypeError("'ModuleNode' object is not callable") return rst diff --git a/imperative/python/megengine/utils/profiler.py b/imperative/python/megengine/utils/profiler.py index 00935adb5d2f936d0e08b78044231c7b793d39b0..1b1f7e5745f178e484ef1c823b368327ab5345e4 100644 --- a/imperative/python/megengine/utils/profiler.py +++ b/imperative/python/megengine/utils/profiler.py @@ -8,13 +8,16 @@ from typing import List from weakref import WeakSet from .. import _atexit +from ..core._imperative_rt.core2 import Tensor as raw_tensor from ..core._imperative_rt.core2 import ( cupti_available, disable_cupti, enable_cupti, full_sync, pop_scope, + pop_scope_with_type, push_scope, + push_scope_with_type, set_python_backtrace_enabled, start_profile, stop_profile, @@ -33,6 +36,7 @@ class Profiler(ContextDecorator): Args: path: default path prefix for profiler to dump. with_backtrace: Whether to record backtrace information for ops. + with_scopes: Whether to keep more scopes to record record module/functional hierarchy. Enabling this option will slow down your program execution. Examples: @@ -70,6 +74,7 @@ class Profiler(ContextDecorator): format: str = "chrome_timeline.json", formats: List[str] = None, with_backtrace: bool = False, + with_scopes: bool = False, **kwargs ) -> None: if not formats: @@ -89,6 +94,8 @@ class Profiler(ContextDecorator): self._options[opt] = int(kwargs.pop(opt, optval)) self._pid = "" self._dump_callback = None + self._api_patcher = None + self._with_scopes = with_scopes if self._options.get("enable_cupti", 0): if cupti_available(): enable_cupti() @@ -110,6 +117,59 @@ class Profiler(ContextDecorator): def directory(self): return self._path + @property + def _patcher(self): + if self._api_patcher != None: + return self._api_patcher + from ..traced_module.module_tracer import Patcher, module_tracer + from ..module import Module + + def wrap_tensormethod_and_functional(origin_fn): + def get_tensormeth_name(obj, func): + tp = obj if isinstance(obj, type) else type(obj) + if not issubclass(tp, raw_tensor): + return None + for cls in tp.mro(): + for k, v in cls.__dict__.items(): + if v == func: + return k + return None + + @wraps(origin_fn) + def wrapped_fn(*args, **kwargs): + methname = ( + get_tensormeth_name(args[0], wrapped_fn) if len(args) > 0 else None + ) + name, scope_type = ( + ("tensor." + methname, "tensor_method") + if methname is not None + else (origin_fn.__name__, "functional") + ) + push_scope_with_type(name, scope_type) + rst = origin_fn(*args, **kwargs) + pop_scope_with_type(name, scope_type) + return rst + + return wrapped_fn + + def wrap_module_call(origin_fn): + @wraps(origin_fn) + def wrapped_fn(*args, **kwargs): + is_builtin_module = module_tracer.is_builtin(type(args[0])) + if not is_builtin_module: + return origin_fn(*args, **kwargs) + name, scope_type = type(args[0]).__name__, "module" + push_scope_with_type(name, scope_type) + rst = origin_fn(*args, **kwargs) + pop_scope_with_type(name, scope_type) + return rst + + return wrapped_fn + + self._api_patcher = Patcher(wrap_tensormethod_and_functional) + self._api_patcher.patch_method(Module, "__call__", wrap_module_call) + return self._api_patcher + @property def formats(self): return list(self._formats) @@ -171,9 +231,14 @@ class Profiler(ContextDecorator): def __enter__(self): self.start() + if self._with_scopes: + self._patcher.__enter__() def __exit__(self, val, tp, trace): self.stop() + if self._with_scopes and self._api_patcher is not None: + self._api_patcher.__exit__(val, tp, trace) + self._api_patcher = None def __call__(self, func): func = super().__call__(func) diff --git a/imperative/python/src/tensor.cpp b/imperative/python/src/tensor.cpp index 36c2e9eb06dcaf8c1719e8eb75cc7771a3edefdf..a93dc43d77ea0045ecaf2221434884a2b2adbe22 100644 --- a/imperative/python/src/tensor.cpp +++ b/imperative/python/src/tensor.cpp @@ -948,6 +948,29 @@ void init_tensor(py::module m) { channel->pop_scope(name); Transformation::pop_scope(name); }); + std::unordered_map str2scopetype = { + {"default", ScopeType::DEFAULT}, + {"module", ScopeType::MODULE}, + {"tensor_method", ScopeType::TENSOR_METHOD}, + {"functional", ScopeType::FUNCTIONAL}, + {"backward", ScopeType::BACKWARD}}; + + m.def("push_scope_with_type", + [channel, str2scopetype](std::string name, std::string type) { + if (str2scopetype.find(type) == str2scopetype.end()) { + throw py::value_error("unsupport scope type"); + } else { + channel->push_scope(name, str2scopetype.find(type)->second); + } + }); + m.def("pop_scope_with_type", + [channel, str2scopetype](std::string name, std::string type) { + if (str2scopetype.find(type) == str2scopetype.end()) { + throw py::value_error("unsupport scope type"); + } else { + channel->pop_scope(name, str2scopetype.find(type)->second); + } + }); m.def("start_profile", [channel](imperative::Profiler::options_t options) { channel->sync(); imperative::Profiler::load_options(std::move(options)); diff --git a/imperative/src/impl/interpreter/commands.h b/imperative/src/impl/interpreter/commands.h index f7729a42bb19c0b56b7d88f2d935a3ec74810759..3066391e12686eb01e496b8a990f226edda11f7f 100644 --- a/imperative/src/impl/interpreter/commands.h +++ b/imperative/src/impl/interpreter/commands.h @@ -4,14 +4,14 @@ #include #include +#include "./stack_manager.h" +#include "./tensor_info.h" #include "megbrain/imperative/backtrace.h" #include "megbrain/imperative/op_def.h" +#include "megbrain/imperative/profiler.h" #include "megbrain/imperative/utils/to_string.h" #include "megbrain/tensor.h" -#include "./stack_manager.h" -#include "./tensor_info.h" - namespace mgb::imperative { namespace interpreter::intl { @@ -125,6 +125,7 @@ struct StopStep { struct PushScope { std::string scope_name; + ScopeType type; template void get_props(TFunctor&& functor) const { @@ -136,7 +137,7 @@ struct PushScope { struct PopScope { std::string scope_name; - + ScopeType type; template void get_props(TFunctor&& functor) const { functor("scope_name", scope_name); diff --git a/imperative/src/impl/interpreter/interpreter_impl.cpp b/imperative/src/impl/interpreter/interpreter_impl.cpp index a5348dd1a03c6206fc96dd5dd652b75652e857dc..e54956b1d51f9b2bbebf5520a649bef83ab63e0c 100644 --- a/imperative/src/impl/interpreter/interpreter_impl.cpp +++ b/imperative/src/impl/interpreter/interpreter_impl.cpp @@ -1356,7 +1356,7 @@ void ChannelImpl::process_one_task(Command& icmd) { } else if constexpr (std::is_same_v) { MGB_RECORD_EVENT(StopStepEvent); } else if constexpr (std::is_same_v) { - MGB_RECORD_EVENT(ScopeEvent, cmd.scope_name); + MGB_RECORD_EVENT(ScopeEvent, cmd.scope_name, cmd.type); } else if constexpr (std::is_same_v) { MGB_RECORD_EVENT(ScopeFinishEvent, cmd.scope_name); } else if constexpr (std::is_same_v) { @@ -1461,15 +1461,15 @@ void ChannelImpl::stop_step() { get_channel_state().stack_manager.dump()}); } -void ChannelImpl::push_scope(std::string name) { +void ChannelImpl::push_scope(std::string name, ScopeType type) { MGB_LOCK_GUARD(m_spin); assert_available(); auto& state = get_channel_state(); state.stack_manager.enter(name); - MGB_RECORD_EVENT(ScopeEvent, name); + MGB_RECORD_EVENT(ScopeEvent, name, type); if (Profiler::is_profiling()) { m_worker.add_task( - {Profiler::next_id(), PushScope{name}, + {Profiler::next_id(), PushScope{name, type}, get_channel_state().stack_manager.dump()}); } else { m_worker.add_task({ @@ -1479,15 +1479,15 @@ void ChannelImpl::push_scope(std::string name) { } } -void ChannelImpl::pop_scope(std::string name) { +void ChannelImpl::pop_scope(std::string name, ScopeType type) { MGB_LOCK_GUARD(m_spin); assert_available(); auto& state = get_channel_state(); state.stack_manager.exit(name); - MGB_RECORD_EVENT(ScopeFinishEvent, name); + MGB_RECORD_EVENT(ScopeFinishEvent, name, type); if (Profiler::is_profiling()) { m_worker.add_task( - {Profiler::next_id(), PopScope{name}, + {Profiler::next_id(), PopScope{name, type}, get_channel_state().stack_manager.dump()}); } else { m_worker.add_task({ diff --git a/imperative/src/impl/interpreter/interpreter_impl.h b/imperative/src/impl/interpreter/interpreter_impl.h index 984b402628f6bee002ee195ce27e424196980080..08c129f5b57dd7aed1d62389cf328acf7f591018 100644 --- a/imperative/src/impl/interpreter/interpreter_impl.h +++ b/imperative/src/impl/interpreter/interpreter_impl.h @@ -65,8 +65,8 @@ struct ChannelImpl : Interpreter::Channel, NonCopyableObj, NonMoveableObj { void stop_profile() override; void stop_step() override; - void push_scope(std::string) override; - void pop_scope(std::string) override; + void push_scope(std::string, ScopeType type = ScopeType::DEFAULT) override; + void pop_scope(std::string, ScopeType type = ScopeType::DEFAULT) override; BackTraceInfoPtr& get_backtrace() override; void set_backtrace(BackTraceInfoPtr bt) override; diff --git a/imperative/src/impl/profiler/events.h b/imperative/src/impl/profiler/events.h index a8e4d511ecd355dfc2f386352ebf7da026eb6b1f..acc904bbd26b31471f6eb51c8cbbbec0ba22eca8 100644 --- a/imperative/src/impl/profiler/events.h +++ b/imperative/src/impl/profiler/events.h @@ -139,7 +139,10 @@ DEF_EVENT(WorkerException, {}); DEF_EVENT(ShapeInfer, { bool success; }); -DEF_DUR_EVENT(Scope, { std::string name; }); +DEF_DUR_EVENT(Scope, { + std::string name; + ScopeType type = ScopeType::DEFAULT; +}); DEF_DUR_EVENT(Sync, { Trace trace; }); diff --git a/imperative/src/impl/profiler/memory_chunk.cpp b/imperative/src/impl/profiler/memory_chunk.cpp index cfa6e3dfbc907be25ec2c1ab1a789ddcc8836057..0fdc48f15d2bfb8b5ba05bcba714244e3b4e560b 100644 --- a/imperative/src/impl/profiler/memory_chunk.cpp +++ b/imperative/src/impl/profiler/memory_chunk.cpp @@ -5,11 +5,10 @@ #include "megbrain/imperative/utils/to_string.h" #include "megbrain/utils/debug.h" +#include "./events.h" #include "./formats.h" #include "./states.h" -#include "./events.h" - namespace mgb::imperative::profiler { class XMLWriter { diff --git a/imperative/src/impl/transformations/eval.cpp b/imperative/src/impl/transformations/eval.cpp index 93e44bbc2b982901e64fae3e5d2cc4ba010ee8e8..400a2264550161a895cead2d98743aee834100da 100644 --- a/imperative/src/impl/transformations/eval.cpp +++ b/imperative/src/impl/transformations/eval.cpp @@ -135,6 +135,12 @@ ValueRefList InterpreterTransformation::apply_transformation( DeviceTensorND dev_tensor; dev_tensor.copy_from(m_channel->get_dev_tensor(input.handle()->handle())); return m_value_type.make(share_handle(m_channel->put(dev_tensor, {}))); + } else if (auto push_scope = op.as()) { + m_channel->push_scope(push_scope->name, push_scope->type); + return {}; + } else if (auto pop_scope = op.as()) { + m_channel->pop_scope(pop_scope->name, pop_scope->type); + return {}; } else { return op.fallback(inputs); } diff --git a/imperative/src/impl/transformations/grad.cpp b/imperative/src/impl/transformations/grad.cpp index 8c68cbd8417ac6e007ad30ba2bdf3209ac1bc5d3..8603bcd18b96d29e30374b3244c66a8af69cb3af 100644 --- a/imperative/src/impl/transformations/grad.cpp +++ b/imperative/src/impl/transformations/grad.cpp @@ -2,11 +2,11 @@ #include +#include #include "megbrain/imperative/graph_cache.h" +#include "megbrain/imperative/profiler.h" #include "megbrain/imperative/resource_manager.h" -#include - namespace mgb { namespace imperative { @@ -232,7 +232,14 @@ void GradKey::backward() { for (auto&& slot : grad_fn->m_slots) { *iter++ = slot.m_grad; } + std::string name = op ? op->name() + "Backward" : "CustomBackward"; + if (Profiler::is_profiling()) { + imperative::apply(PushScope(name, ScopeType::BACKWARD), Span(nullptr, nullptr)); + } backward(grads, grad_receiver); + if (Profiler::is_profiling()) { + imperative::apply(PopScope(name, ScopeType::BACKWARD), Span(nullptr, nullptr)); + } } }, grad_fn->m_backward); // clang-format on diff --git a/imperative/src/impl/transformations/lazy.cpp b/imperative/src/impl/transformations/lazy.cpp index 01527220c27c14b45482b7620c2f937b76a85c27..33e23f54c22c584987c86c8c6402cdd13bb09aa0 100644 --- a/imperative/src/impl/transformations/lazy.cpp +++ b/imperative/src/impl/transformations/lazy.cpp @@ -172,6 +172,8 @@ ValueRefList LazyEvalTransformation::apply_transformation( } else { return imperative::apply(op, inputs); } + } else if (op.is() || op.is()) { + return {}; } else { return op.fallback(inputs); } diff --git a/imperative/src/include/megbrain/imperative/basic_operators.h b/imperative/src/include/megbrain/imperative/basic_operators.h index e81d11e47d6a13fae954e20e0c0b6d80f576f75a..cca005516c02079302bf9a59853d54602417081c 100644 --- a/imperative/src/include/megbrain/imperative/basic_operators.h +++ b/imperative/src/include/megbrain/imperative/basic_operators.h @@ -5,6 +5,7 @@ #include "megbrain/imperative/op_def.h" #include "megbrain/imperative/operator.h" +#include "megbrain/imperative/profiler.h" #include "megbrain/imperative/utils/data_format.h" #include "megbrain/imperative/utils/helper.h" #include "megbrain/imperative/utils/value_shape.h" @@ -206,6 +207,24 @@ public: std::string to_string() const override; }; +class PushScope final : public OperatorImpl { +public: + std::string name; + ScopeType type; + PushScope(std::string name, ScopeType type) : name{std::move(name)}, type{type} {}; + std::string raw_type() const { return "PushScope"; } + std::string to_string() const override { return "PushScope"; } +}; + +class PopScope final : public OperatorImpl { +public: + std::string name; + ScopeType type; + PopScope(std::string name, ScopeType type) : name{std::move(name)}, type{type} {}; + std::string raw_type() const { return "PopScope"; } + std::string to_string() const override { return "PopScope"; } +}; + class DupTensor final : public OperatorImpl { public: std::string to_string() const override { return "DupTensor"; } diff --git a/imperative/src/include/megbrain/imperative/interpreter.h b/imperative/src/include/megbrain/imperative/interpreter.h index d2a0a503a5e6dce555d665ba68123896987b1370..af3decc005f4de4f4516bb79ae045b7ea7d7bc54 100644 --- a/imperative/src/include/megbrain/imperative/interpreter.h +++ b/imperative/src/include/megbrain/imperative/interpreter.h @@ -5,6 +5,7 @@ #include "./backtrace.h" #include "megbrain/imperative/op_def.h" +#include "megbrain/imperative/profiler.h" namespace mgb::imperative::interpreter { @@ -63,12 +64,13 @@ struct Interpreter { virtual void stop_profile() = 0; virtual void stop_step() = 0; - virtual void push_scope(std::string name) = 0; - virtual void pop_scope(std::string name) = 0; - virtual BackTraceInfoPtr& get_backtrace() = 0; virtual void set_backtrace(BackTraceInfoPtr bt) = 0; virtual void clear_backtrace() = 0; + virtual void push_scope( + std::string name, ScopeType type = ScopeType::DEFAULT) = 0; + virtual void pop_scope( + std::string name, ScopeType type = ScopeType::DEFAULT) = 0; }; virtual std::unique_ptr create_channel() = 0; diff --git a/imperative/src/include/megbrain/imperative/profiler.h b/imperative/src/include/megbrain/imperative/profiler.h index f51b7adc8e45fa58ea649e6f7ffc18bdefda2a87..d0e469a6f1d5595205a4eacb37f45aa08485e6f0 100644 --- a/imperative/src/include/megbrain/imperative/profiler.h +++ b/imperative/src/include/megbrain/imperative/profiler.h @@ -41,6 +41,14 @@ public: static std::shared_ptr record_device(CompNode device); }; +enum ScopeType { + DEFAULT = 0, + MODULE = 1, + TENSOR_METHOD = 2, + FUNCTIONAL = 3, + BACKWARD = 4, +}; + class Profiler { public: struct Record {