/** * \file src/plugin/impl/profiler.cpp * 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. */ #include "megbrain/plugin/profiler.h" #include "megbrain/plugin/opr_footprint.h" #if MGB_ENABLE_JSON #include "megbrain/graph/event.h" #include "megbrain/opr/io.h" #include "megbrain/system.h" using namespace mgb; using namespace cg; MGB_TYPEINFO_OBJ_IMPL(opr_profile::OprProfileHolder); GraphProfiler::GraphProfiler(cg::ComputingGraph* graph) : PluginBase(graph) { graph->options() .user_data.get_user_data_or_create(); using namespace cg::event; auto on_seq_start = [this](CompSeqExecBeforeStart const& event) { m_used_comp_node = event.used_comp_node; }; auto on_opr_start = [this](OprExecStart const& event) { ensure_start_time(); if (!opr_filter(event.opr)) return; OperatorNodeBase* opr = event.opr; for (auto&& comp_node : get_opr_comp_node_set(event.opr)) { auto runner = [this, opr, comp_node]() { MGB_LOCK_GUARD(m_mtx); auto&& hev = m_host_time[{opr, std::this_thread::get_id()}]; hev.start = m_timer.get_secs(); hev.kern = -1; record_event(m_kern_event[{opr, comp_node}].start, comp_node); }; event.env->dispatch_on_comp_node(comp_node, runner); } }; auto on_opr_finish = [this](OprExecFinished const& event) { OperatorNodeBase* opr = event.opr; if (!opr_filter(opr)) return; for (auto&& comp_node : get_opr_comp_node_set(event.opr)) { auto runner = [this, opr]() { MGB_LOCK_GUARD(m_mtx); m_host_time[{opr, std::this_thread::get_id()}].end = m_timer.get_secs(); }; event.env->dispatch_on_comp_node(comp_node, runner); } }; auto on_before_kern = [this](BeforeKernel const& event) { if (!opr_filter(event.opr)) return; auto footprint = m_opr_footprint_ptr->calc_footprint(event.opr); CompNodeEventPtr* evptr; { MGB_LOCK_GUARD(m_mtx); m_opr_fp_rst.emplace(event.opr, footprint); auto&& hev = m_host_time[{event.opr, std::this_thread::get_id()}]; if (hev.kern == -1) { hev.kern = m_timer.get_secs(); } evptr = &m_kern_event[{event.opr, event.comp_node}].kern; } record_event(*evptr, event.comp_node); }; auto on_after_kern = [this](AfterKernel const& event) { if (!opr_filter(event.opr)) return; CompNodeEventPtr* evptr; { MGB_LOCK_GUARD(m_mtx); evptr = &m_kern_event[{event.opr, event.comp_node}].end; } record_event(*evptr, event.comp_node); }; auto on_graph_compile = [this](const CompSeqOrderDetermined&) { // clear status after graph recompilation m_host_time.clear(); m_kern_event.clear(); m_opr_fp_rst.clear(); m_start_of_time = None; }; auto&& ev = graph->event(); add_event_handler( ev.register_receiver(on_seq_start)); add_event_handler(ev.register_receiver(on_opr_start)); add_event_handler(ev.register_receiver(on_opr_finish)); add_event_handler(ev.register_receiver(on_before_kern)); add_event_handler(ev.register_receiver(on_after_kern)); add_event_handler( ev.register_receiver(on_graph_compile)); } GraphProfiler::~GraphProfiler() noexcept { auto wait = [](const CompNodeEventPtr& ev) { if (ev) ev->host_wait(); }; for (auto&& i : m_kern_event) { wait(i.second.start); wait(i.second.kern); wait(i.second.end); } m_owner_graph->options() .user_data.pop_user_data(); } void GraphProfiler::ensure_start_time() { if (!m_start_of_time.valid()) { // set up for the first time m_start_of_time = CompNode::UnorderedMap>(); for (auto i: *m_used_comp_node) { i.sync(); auto&& event = m_start_of_time.val()[i]; event = i.create_event(CompNode::Event::NEED_TIMER); event->record(); } } } void GraphProfiler::record_event(CompNodeEventPtr& dest, CompNode comp_node) { if (!dest) dest = comp_node.create_event(CompNode::Event::NEED_TIMER); dest->record(); } bool GraphProfiler::opr_filter(cg::OperatorNodeBase* opr) { static bool only_wait = MGB_GETENV("MGB_PROFILE_ONLY_WAIT"); if (!only_wait) return true; if (!opr->input_waiting_spec().empty()) return true; auto type = opr->dyn_typeinfo(); return type == opr::Copy::typeinfo() || type == opr::Host2DeviceCopy::typeinfo(); } std::shared_ptr GraphProfiler::to_json() const { using namespace json; auto dev_prof = Object::make(); auto visit_json_obj = [](Object& obj, const std::string& key) -> Object& { auto&& v = obj[key]; if (!v) v = Object::make(); return *static_cast(v.get()); }; for (auto&& kern_ev : m_kern_event) { auto&& opr_prof = visit_json_obj(*dev_prof, kern_ev.first.first->id_str()); auto comp_node = kern_ev.first.second; auto&& event = kern_ev.second; auto&& start = m_start_of_time->at(comp_node); event.end->host_wait(); opr_prof[comp_node.to_string()] = Object::make({ {"start", Number::make(start->elapsed_time_until(*event.start))}, {"kern", Number::make(start->elapsed_time_until(*event.kern))}, {"end", Number::make(start->elapsed_time_until(*event.end))}, }); } auto host_prof = Object::make(); for (auto&& tpair : m_host_time) { auto&& opr_prof = visit_json_obj(*host_prof, tpair.first.first->id_str()); auto&& ev = tpair.second; opr_prof[sys::get_thread_name(tpair.first.second)] = Object::make({{"start", Number::make(ev.start)}, {"kern", Number::make(ev.kern)}, {"end", Number::make(ev.end)}}); } auto opr_fp = Object::make(); for (auto&& tpair : m_opr_fp_rst) { auto&& opr_fp_item = *static_cast(opr_fp.get()); opr_fp_item[tpair.first->id_str()] = tpair.second.to_json(); } auto pf_holder_pair = m_owner_graph->options() .user_data.get_user_data(); mgb_assert(pf_holder_pair.second, "UserData OprProfileHolder not exist."); auto opr_internal_pf = Object::make(); if ((pf_holder_pair.first[0]->id2object_map).size()) { for (auto&& pf_pair : pf_holder_pair.first[0]->id2object_map) { auto&& opr_itnl_pf_item = *static_cast(opr_internal_pf.get()); opr_itnl_pf_item[pf_pair.first->id_str()] = pf_pair.second; } } return Object::make({{"device", dev_prof}, {"host", host_prof}, {"opr_footprint", opr_fp}, {"opr_internal_pf", opr_internal_pf}}); } #endif // MGB_ENABLE_JSON // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}