// Copyright (C) 2012-2017 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/core/profiler.hh" #include "nv/core/time.hh" #include "nv/core/logging.hh" #include #include using namespace nv; #if NV_COMPILER == NV_MSVC #define snprintf sprintf_s #endif profiler::profiler() { m_root = new node( "root"_hls64, nullptr ); m_root->start(); m_current = m_root; } profiler::~profiler() { delete m_root; } void profiler::start_profile( string_type&& tag ) { if ( tag != m_current->m_tag ) { m_current = m_current->request_child( ::nv::move( tag ) ); } m_current->start(); } void profiler::stop_profile() { if ( m_current->stop() ) { m_current = m_current->get_parent(); } } profiler::node::node( string_view tag, node* parent ) : m_tag( ::nv::move( tag ) ) , m_parent( parent ) , m_recusion( 0 ) , m_calls( 0 ) , m_start_time_us( 0 ) , m_total_time_us( 0 ) { } profiler::node* profiler::node::request_child( string_type&& tag ) { auto it = m_children.find( tag ); if ( it != m_children.end() ) return it->second; else { node* result = new node( tag, this ); m_children.assign( ::nv::move( tag ), result ); return result; } } void profiler::node::start() { m_calls++; m_recusion++; if ( m_recusion == 1 ) { m_start_time_us = get_system_us(); } } bool profiler::node::stop() { m_recusion--; if ( m_recusion == 0 ) { uint64 stop_time_us = get_system_us(); uint64 elapsed_us = stop_time_us - m_start_time_us; m_total_time_us += elapsed_us; return true; } return false; } nv::profiler::node::~node() { for ( const auto& pair : m_children ) { delete pair.second; } } void profiler::log_report() { m_root->stop(); NV_LOG_INFO( "-- PROFILER REPORT -------------------------------------" ); char buffer[128]; snprintf( buffer, 128, "%-23s %6s %6s %9s %6s", "TAG", "%PARNT", "CALLS", "TOTAL(ms)", "AVG(ms)" ); NV_LOG_INFO( string_view( buffer, static_cast< uint32 >( nvstrlen( buffer ) ) ) ); log_node_children( 0, m_root ); NV_LOG_INFO( "-- PROFILER REPORT END ---------------------------------" ); m_root->start(); } void profiler::log_node_children( uint32 indent, const node* n ) { char buffer[128]; for ( const auto& pair : n->m_children ) { const node* c = pair.second; if ( c->m_calls > 0 ) { f64 ftotal_time_us = static_cast( c->m_total_time_us ); f64 pparent = ( ftotal_time_us / static_cast( c->m_parent->m_total_time_us ) ) * 100.f; uint32 calls = c->m_calls; f64 total_ms = ftotal_time_us / 1000.f; f64 avg_ms = ( ftotal_time_us / static_cast( c->m_calls ) ) / 1000.f; if ( indent > 0 ) nvmemset( buffer, '-', indent ); snprintf( buffer + indent, 128 - indent, "%*.*s %6.2f %6d %9.2f %6.2f", indent - 23, 23 - indent, c->m_tag.data(), pparent, calls, total_ms, avg_ms ); NV_LOG_INFO( string_view( buffer, static_cast< uint32 >( nvstrlen( buffer ) ) ) ); if ( c->m_children.size() > 0 ) { log_node_children( indent + 1, c ); } } } }