#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<opr_profile::OprProfileHolder>();
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&) {
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<CompSeqExecBeforeStart>(on_seq_start));
add_event_handler(ev.register_receiver<OprExecStart>(on_opr_start));
add_event_handler(ev.register_receiver<OprExecFinished>(on_opr_finish));
add_event_handler(ev.register_receiver<BeforeKernel>(on_before_kern));
add_event_handler(ev.register_receiver<AfterKernel>(on_after_kern));
add_event_handler(ev.register_receiver<CompSeqOrderDetermined>(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<opr_profile::OprProfileHolder>();
}
void GraphProfiler::ensure_start_time() {
if (!m_start_of_time.valid()) {
m_start_of_time = CompNode::UnorderedMap<std::unique_ptr<CompNode::Event>>();
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<json::Object> 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<Object*>(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<Object*>(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<opr_profile::OprProfileHolder>();
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<Object*>(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