diff --git a/imperative/python/src/module_trace.h b/imperative/python/src/module_trace.h index 8a9f9ce8..90706e98 100644 --- a/imperative/python/src/module_trace.h +++ b/imperative/python/src/module_trace.h @@ -14,7 +14,6 @@ #include #include "megbrain/imperative/transformations/trace.h" #include "megbrain/imperative/utils/map.h" -#include "megbrain/imperative/utils/stats.h" #include "./tensor.h" diff --git a/imperative/python/src/tensor.cpp b/imperative/python/src/tensor.cpp index 0f219745..efbb0509 100644 --- a/imperative/python/src/tensor.cpp +++ b/imperative/python/src/tensor.cpp @@ -23,9 +23,9 @@ #include "megbrain/imperative/transformations/symbol.h" #include "megbrain/imperative/transformations/trace.h" #include "megbrain/imperative/utils/map.h" -#include "megbrain/imperative/utils/stats.h" #include "megbrain/opr/io.h" #include "megbrain/plugin/profiler.h" +#include "megbrain/utils/stats.h" #include "./common.h" #include "./grad.h" @@ -1367,9 +1367,9 @@ void init_tensor(py::module m) { return reprs; }); - m.def("print_stats", [] { imperative::Stats::print(); }); + m.def("print_stats", [] { Stats::print(); }); - m.def("reset_stats", [] { imperative::Stats::reset(); }); + m.def("reset_stats", [] { Stats::reset(); }); m.def("_get_convert_inputs", []() -> bool { return DTypePromoteCfg::convert_input_enabled; }); diff --git a/imperative/python/src/tensor_utils.cpp b/imperative/python/src/tensor_utils.cpp index a424eee0..1b6fd944 100644 --- a/imperative/python/src/tensor_utils.cpp +++ b/imperative/python/src/tensor_utils.cpp @@ -21,7 +21,6 @@ #include "megbrain/imperative/transformations/symbol.h" #include "megbrain/imperative/transformations/trace.h" #include "megbrain/imperative/utils/map.h" -#include "megbrain/imperative/utils/stats.h" #include "megbrain/opr/io.h" #include "megbrain/plugin/profiler.h" diff --git a/imperative/src/impl/dispatch.cpp b/imperative/src/impl/dispatch.cpp index 7f94d314..c982c6c2 100644 --- a/imperative/src/impl/dispatch.cpp +++ b/imperative/src/impl/dispatch.cpp @@ -14,7 +14,6 @@ #include "megbrain/imperative/utils/debug.h" #include "megbrain/imperative/utils/helper.h" #include "megbrain/imperative/utils/map.h" -#include "megbrain/imperative/utils/stats.h" namespace mgb { namespace imperative { diff --git a/imperative/src/impl/interpreter/interpreter_impl.cpp b/imperative/src/impl/interpreter/interpreter_impl.cpp index 6a5af98f..5b556430 100644 --- a/imperative/src/impl/interpreter/interpreter_impl.cpp +++ b/imperative/src/impl/interpreter/interpreter_impl.cpp @@ -19,7 +19,6 @@ #include "megbrain/imperative/ops/backward_graph.h" #include "megbrain/imperative/ops/opr_attr.h" #include "megbrain/imperative/ops/utility.h" -#include "megbrain/imperative/utils/stats.h" #include "megbrain/imperative/utils/to_string.h" #include "../blob_manager_impl.h" diff --git a/imperative/src/impl/ops/elemwise.cpp b/imperative/src/impl/ops/elemwise.cpp index 621e7f34..7d19b5e3 100644 --- a/imperative/src/impl/ops/elemwise.cpp +++ b/imperative/src/impl/ops/elemwise.cpp @@ -11,7 +11,6 @@ #include "megbrain/imperative/opr_utility.h" #include "megbrain/imperative/ops/autogen.h" -#include "megbrain/imperative/utils/stats.h" #include "megbrain/opr/basic_arith.h" #include "megbrain/opr/utility.h" diff --git a/imperative/src/impl/ops/pooling.cpp b/imperative/src/impl/ops/pooling.cpp index c7b4c59e..8281ac0f 100644 --- a/imperative/src/impl/ops/pooling.cpp +++ b/imperative/src/impl/ops/pooling.cpp @@ -11,7 +11,6 @@ #include "megbrain/opr/dnn/pooling.h" #include "megbrain/imperative/ops/autogen.h" -#include "megbrain/imperative/utils/stats.h" #include "megbrain/opr/utility.h" #include "megbrain/opr/internal/megdnn_opr_wrapper.h" diff --git a/imperative/src/impl/transformation.cpp b/imperative/src/impl/transformation.cpp index 32c0c4b3..2b3b326f 100644 --- a/imperative/src/impl/transformation.cpp +++ b/imperative/src/impl/transformation.cpp @@ -1,5 +1,4 @@ #include "megbrain/imperative/transformation.h" -#include "megbrain/imperative/utils/stats.h" namespace mgb { namespace imperative { diff --git a/imperative/src/impl/transformations/eval.cpp b/imperative/src/impl/transformations/eval.cpp index d0478921..5fda5437 100644 --- a/imperative/src/impl/transformations/eval.cpp +++ b/imperative/src/impl/transformations/eval.cpp @@ -10,7 +10,6 @@ */ #include "megbrain/imperative/transformations/eval.h" -#include "megbrain/imperative/utils/stats.h" namespace mgb { namespace imperative { diff --git a/imperative/src/impl/transformations/grad.cpp b/imperative/src/impl/transformations/grad.cpp index b7660236..54e93100 100644 --- a/imperative/src/impl/transformations/grad.cpp +++ b/imperative/src/impl/transformations/grad.cpp @@ -15,7 +15,6 @@ #include "megbrain/imperative/graph_cache.h" #include "megbrain/imperative/resource_manager.h" -#include "megbrain/imperative/utils/stats.h" #include diff --git a/imperative/src/impl/transformations/scalar.cpp b/imperative/src/impl/transformations/scalar.cpp index 89ee1f46..2d6fd7c9 100644 --- a/imperative/src/impl/transformations/scalar.cpp +++ b/imperative/src/impl/transformations/scalar.cpp @@ -13,7 +13,6 @@ #include "megbrain/imperative/ops/autogen.h" #include "megbrain/imperative/ops/utility.h" -#include "megbrain/imperative/utils/stats.h" namespace mgb { namespace imperative { diff --git a/imperative/src/include/megbrain/imperative/utils/stats.h b/imperative/src/include/megbrain/imperative/utils/stats.h deleted file mode 100644 index 48d36171..00000000 --- a/imperative/src/include/megbrain/imperative/utils/stats.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace mgb { -namespace imperative { -namespace stats { - -#define MGE_ENABLE_STATS 0 - -class Timer { -public: - using clock_t = std::chrono::system_clock; - -private: - clock_t::duration m_duration = clock_t::duration{0}; - size_t m_timing = 0; - std::string m_name; - uint64_t m_count = 0; - size_t m_enabled = 1; - bool m_default_enabled = true; - - struct TimeScopeRecursive { - Timer& timer; - clock_t::time_point start; - bool released = false; - - TimeScopeRecursive(Timer& timer) : timer(timer) { - if (timer.m_enabled && !timer.m_timing++) { - start = clock_t::now(); - } - } - - ~TimeScopeRecursive() { release(); } - - void release() { - if (released) { - return; - } - if (timer.m_enabled) { - if (!--timer.m_timing) { - auto duration = (clock_t::now() - start); - timer.m_duration += duration; - } - timer.m_count++; - } - released = true; - } - }; - - struct EnableScope { - Timer& timer; - bool released = false; - - EnableScope(Timer& timer) : timer(timer) { timer.m_enabled++; } - - ~EnableScope() { release(); } - - void release() { - if (released) { - return; - } - timer.m_enabled--; - released = true; - } - }; - -public: - Timer(std::string name, bool default_enabled = true); - - std::string name() { return m_name; } - auto time_scope_recursive() { return TimeScopeRecursive(*this); }; - auto enable_scope() { return EnableScope(*this); } - void reset() { - m_duration = clock_t::duration{0}; - m_count = 0; - m_enabled = m_default_enabled ? 1 : 0; - } - - clock_t::duration get() const { return m_duration; } - uint64_t count() const { return m_count; } -}; -} // namespace stats - -struct Stats { - struct TimerNode { - std::map> children; - stats::Timer* timer = nullptr; - - TimerNode() {} - }; - - static inline TimerNode sm_root; - - // register your timers here - // for example: - // - // static inline stats::Timer mytimer; - // - // then use MGE_TIMER_SCOPE(mytimer) to collect durations in your code - - static std::pair print_node( - std::string name, TimerNode& node, size_t indent = 0) { - auto print_indent = [&] { - for (size_t i = 0; i < indent; ++i) { - printf(" "); - } - }; - long ns = 0, count = 0; - if (auto* timer = node.timer) { - print_indent(); - printf("%s costs %'ld ns, hits %'ld times\n", name.c_str(), - (long)timer->get().count(), (long)timer->count()); - ns = timer->get().count(); - count = timer->count(); - } - if (!node.children.empty()) { - bool collect_children = node.timer == nullptr; - if (collect_children) { - print_indent(); - printf("%s:\n", name.c_str()); - } - long ns = 0, count = 0; - for (auto&& child : node.children) { - auto [child_ns, child_count] = - print_node(child.first, *child.second, indent + 4); - if (collect_children) { - ns += child_ns; - count += child_count; - } - } - if (collect_children) { - print_indent(); - printf("total costs %'ld ns, hits %'ld times\n", ns, count); - } - } - return {ns, count}; - } - - static void print() { - for (auto&& child : sm_root.children) { - print_node(child.first, *child.second); - } - } - - static void reset() { - auto reset_node = [](TimerNode& node, auto&& reset_node) -> void { - if (auto* timer = node.timer) { - timer->reset(); - } - for (auto&& child : node.children) { - reset_node(*child.second, reset_node); - } - }; - reset_node(sm_root, reset_node); - } -}; - -inline stats::Timer::Timer(std::string name, bool default_enabled) - : m_name(name), m_default_enabled(default_enabled) { - std::vector terms; - Stats::TimerNode* node = &Stats::sm_root; - while (true) { - auto pos = name.find("."); - if (pos == std::string::npos) { - auto& child = node->children[name]; - child = std::make_unique(); - node = child.get(); - node->timer = this; - break; - } else { - auto& child = node->children[name.substr(0, pos)]; - if (!child) { - child = std::make_unique(); - } - node = child.get(); - name = name.substr(pos + 1); - } - } -} - -#if MGE_ENABLE_STATS -#define MGE_TIMER_SCOPE(name) auto name = Stats::name.time_scope_recursive() -#define MGE_TIMER_SCOPE_RELEASE(name) name.release() -#define MGE_TIMER_SCOPE_ENABLE(name) auto name = Stats::name.enable_scope() -#else -#define MGE_TIMER_SCOPE(name) (void)0 -#define MGE_TIMER_SCOPE_RELEASE(name) (void)0 -#define MGE_TIMER_SCOPE_ENABLE(name) (void)0 -#endif - -} // namespace imperative -} // namespace mgb diff --git a/imperative/src/include/megbrain/imperative/value.h b/imperative/src/include/megbrain/imperative/value.h index 21541443..c877d37f 100644 --- a/imperative/src/include/megbrain/imperative/value.h +++ b/imperative/src/include/megbrain/imperative/value.h @@ -23,7 +23,6 @@ #include "megbrain/imperative/utils/debug.h" #include "megbrain/imperative/utils/local_ptr.h" #include "megbrain/imperative/utils/span.h" -#include "megbrain/imperative/utils/stats.h" namespace mgb { namespace imperative { diff --git a/src/core/impl/utils/stats.cpp b/src/core/impl/utils/stats.cpp new file mode 100644 index 00000000..93c5ffd4 --- /dev/null +++ b/src/core/impl/utils/stats.cpp @@ -0,0 +1,89 @@ +#include "megbrain/utils/stats.h" + +namespace mgb { + +Stats::TimerNode Stats::sm_root; + +stats::Timer& Stats::get_timer(std::string name) { + auto full_name = name; + Stats::TimerNode* node = &Stats::sm_root; + while (true) { + auto pos = name.find("_"); + if (pos == std::string::npos) { + auto& child = node->children[name]; + child = std::make_unique(); + node = child.get(); + auto& timer = node->timer; + if (!timer) { + timer = std::make_unique(full_name); + } + return *timer; + } else { + auto& child = node->children[name.substr(0, pos)]; + if (!child) { + child = std::make_unique(); + } + node = child.get(); + name = name.substr(pos + 1); + } + } +} + +std::pair Stats::print_node( + std::string name, TimerNode& node, size_t indent) { + auto print_indent = [&] { + for (size_t i = 0; i < indent; ++i) { + printf(" "); + } + }; + long ns = 0, count = 0; + if (auto& timer = node.timer) { + print_indent(); + printf("%s costs %'ld ns, hits %'ld times\n", name.c_str(), + (long)timer->get().count(), (long)timer->count()); + ns = timer->get().count(); + count = timer->count(); + } + if (!node.children.empty()) { + bool collect_children = node.timer == nullptr; + if (collect_children) { + print_indent(); + printf("%s:\n", name.c_str()); + } + long ns = 0, count = 0; + for (auto&& child : node.children) { + auto&& child_res = print_node(child.first, *child.second, indent + 4); + auto&& child_ns = child_res.first; + auto&& child_count = child_res.second; + if (collect_children) { + ns += child_ns; + count += child_count; + } + } + if (collect_children) { + print_indent(); + printf("total costs %'ld ns, hits %'ld times\n", ns, count); + } + } + return {ns, count}; +} + +void Stats::print() { + for (auto&& child : sm_root.children) { + print_node(child.first, *child.second); + } +} + +void Stats::reset() { + auto reset_node = [](TimerNode& node, auto&& reset_node) -> void { + if (auto& timer = node.timer) { + timer->reset(); + } + for (auto&& child : node.children) { + reset_node(*child.second, reset_node); + } + }; + reset_node(sm_root, reset_node); +} + +} // namespace mgb \ No newline at end of file diff --git a/src/core/include/megbrain/utils/stats.h b/src/core/include/megbrain/utils/stats.h new file mode 100644 index 00000000..97ba80c4 --- /dev/null +++ b/src/core/include/megbrain/utils/stats.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "megbrain/common.h" + +namespace mgb { +namespace stats { + +#define MGE_ENABLE_STATS 1 + +class Timer { +public: + using clock_t = std::chrono::system_clock; + +private: + clock_t::duration m_duration = clock_t::duration{0}; + size_t m_timing = 0; + std::string m_name; + uint64_t m_count = 0; + size_t m_enabled = 1; + bool m_default_enabled = true; + + struct TimeScopeRecursive { + Timer& timer; + clock_t::time_point start; + bool released = false; + + TimeScopeRecursive(Timer& timer) : timer(timer) { + if (timer.m_enabled && !timer.m_timing++) { + start = clock_t::now(); + } + } + + ~TimeScopeRecursive() { release(); } + + void release() { + if (released) { + return; + } + if (timer.m_enabled) { + if (!--timer.m_timing) { + auto duration = (clock_t::now() - start); + timer.m_duration += duration; + } + timer.m_count++; + } + released = true; + } + }; + + struct EnableScope { + Timer& timer; + bool released = false; + + EnableScope(Timer& timer) : timer(timer) { timer.m_enabled++; } + + ~EnableScope() { release(); } + + void release() { + if (released) { + return; + } + timer.m_enabled--; + released = true; + } + }; + +public: + Timer(std::string name, bool default_enabled = true) + : m_name(name), m_default_enabled(default_enabled){}; + + std::string name() { return m_name; } + auto time_scope_recursive() { return TimeScopeRecursive(*this); }; + auto enable_scope() { return EnableScope(*this); } + void reset() { + m_duration = clock_t::duration{0}; + m_count = 0; + m_enabled = m_default_enabled ? 1 : 0; + } + + clock_t::duration get() const { return m_duration; } + uint64_t count() const { return m_count; } +}; +} // namespace stats + +struct Stats { +private: + struct TimerNode { + std::map> children; + std::unique_ptr timer; + }; + + static TimerNode sm_root; + + // don't register your timers here + // use MGE_TIMER_SCOPE(mytimer) to collect durations in your code +public: + MGE_WIN_DECLSPEC_FUC static stats::Timer& get_timer(std::string name); + + MGE_WIN_DECLSPEC_FUC static std::pair print_node( + std::string name, TimerNode& node, size_t indent = 0); + + MGE_WIN_DECLSPEC_FUC static void print(); + + MGE_WIN_DECLSPEC_FUC static void reset(); +}; + +#if MGE_ENABLE_STATS + +#define MGE_TIMER_SCOPE(name) \ + static auto& _timer_##name = mgb::Stats::get_timer(#name); \ + auto name = _timer_##name.time_scope_recursive() + +#define MGE_TIMER_SCOPE_RELEASE(name) name.release() + +#define MGE_TIMER_SCOPE_ENABLE(name) \ + static auto& _timer_##name = mgb::Stats::get_timer(#name); \ + auto name = _timer_##name.enable_scope() + +#else + +#define MGE_TIMER_SCOPE(name) (void)0 +#define MGE_TIMER_SCOPE_RELEASE(name) (void)0 +#define MGE_TIMER_SCOPE_ENABLE(name) (void)0 + +#endif + +} // namespace mgb