From f195e77be7ab1ca6d6b66127842745aeb3321ba4 Mon Sep 17 00:00:00 2001 From: Irlan Date: Mon, 1 Apr 2019 11:03:12 -0300 Subject: [PATCH] improve profiler --- examples/testbed/framework/json_profiler.h | 12 +- examples/testbed/framework/main.cpp | 18 ++- examples/testbed/framework/model.cpp | 10 +- examples/testbed/framework/model.h | 17 ++- examples/testbed/framework/profiler.h | 2 + .../testbed/framework/recorder_profiler.cpp | 121 +++++++++++------- .../testbed/framework/recorder_profiler.h | 33 +++-- examples/testbed/framework/testbed_profiler.h | 82 ------------ 8 files changed, 133 insertions(+), 162 deletions(-) delete mode 100644 examples/testbed/framework/testbed_profiler.h diff --git a/examples/testbed/framework/json_profiler.h b/examples/testbed/framework/json_profiler.h index dbca993..8266302 100644 --- a/examples/testbed/framework/json_profiler.h +++ b/examples/testbed/framework/json_profiler.h @@ -19,6 +19,8 @@ #ifndef JSON_PROFILER_H #define JSON_PROFILER_H +#include + #include #include @@ -33,19 +35,19 @@ using namespace rapidjson; // Say chrome://tracing to the web browser and load the file // This file is by default called "profile.json". Any name can be given. // For implementation details, see json_profile.cpp. -class JsonProfiler +class JsonProfiler : public ProfilerListener { public: JsonProfiler(); ~JsonProfiler(); - void BeginEvents(); + void BeginEvents() override; - void EndEvents(); + void EndEvents() override; - void BeginEvent(const char* name, float64 time); + void BeginEvent(const char* name, float64 time) override; - void EndEvent(const char* name, float64 time); + void EndEvent(const char* name, float64 time) override; private: FILE * m_file; FileWriteStream* m_stream; diff --git a/examples/testbed/framework/main.cpp b/examples/testbed/framework/main.cpp index 7b466ba..1ff1c07 100644 --- a/examples/testbed/framework/main.cpp +++ b/examples/testbed/framework/main.cpp @@ -101,6 +101,8 @@ static void Run() glfwGetWindowSize(g_window, &w, &h); g_view->Event_SetWindowSize(u32(w), u32(h)); + b3StackArray records; + while (glfwWindowShouldClose(g_window) == 0) { g_profiler->Begin(); @@ -120,14 +122,10 @@ static void Run() if (g_settings->drawProfile) { - const b3Array& records = g_recorderProfiler->GetRecords(); for (u32 i = 0; i < records.Count(); ++i) { - const ProfilerRecord& r = records[i]; - if (r.call != 0) - { - g_draw->DrawString(b3Color_white, "%s %.4f (%.4f) [ms]", r.name, r.elapsed, r.maxElapsed); - } + ProfilerRecord* r = records[i]; + g_draw->DrawString(b3Color_white, "%s %.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", r->name, r->elapsed, r->minElapsed, r->maxElapsed, r->callCount); } } @@ -138,6 +136,14 @@ static void Run() g_view->EndInterface(); g_profiler->EndScope(); + + g_recorderProfiler->BuildRecords(); + + if (g_settings->drawProfile) + { + records.Resize(0); + g_recorderProfiler->BuildSortedRecords(records); + } g_profiler->End(); diff --git a/examples/testbed/framework/model.cpp b/examples/testbed/framework/model.cpp index 37c9aab..f3f2c8c 100644 --- a/examples/testbed/framework/model.cpp +++ b/examples/testbed/framework/model.cpp @@ -26,8 +26,11 @@ Model::Model() g_draw = &m_draw; g_camera = &m_camera; g_profiler = &m_profiler; - g_recorderProfiler = &m_profilerListener.m_recorderListener; - g_profilerListener = &m_profilerListener; + g_recorderProfiler = &m_recorderProfiler; + +#if (PROFILE_JSON == 1) + g_profilerListener = &m_jsonListener; +#endif m_test = nullptr; @@ -54,7 +57,10 @@ Model::~Model() g_camera = nullptr; g_profiler = nullptr; g_recorderProfiler = nullptr; + +#if (PROFILE_JSON == 1) g_profilerListener = nullptr; +#endif delete m_test; } diff --git a/examples/testbed/framework/model.h b/examples/testbed/framework/model.h index 2580055..6371131 100644 --- a/examples/testbed/framework/model.h +++ b/examples/testbed/framework/model.h @@ -20,7 +20,15 @@ #define MODEL_H #include -#include +#include +#include + +// Set to 1 to write profile events into a .json file. Set to 0 otherwise. +#define PROFILE_JSON 1 + +#if (PROFILE_JSON == 1) +#include +#endif class Test; @@ -63,7 +71,12 @@ private: Draw m_draw; Camera m_camera; Profiler m_profiler; - TestbedProfiler m_profilerListener; + RecorderProfiler m_recorderProfiler; + +#if (PROFILE_JSON == 1) + JsonProfiler m_jsonListener; +#endif + Test* m_test; bool m_setTest; bool m_pause; diff --git a/examples/testbed/framework/profiler.h b/examples/testbed/framework/profiler.h index 004f878..aa70633 100644 --- a/examples/testbed/framework/profiler.h +++ b/examples/testbed/framework/profiler.h @@ -59,6 +59,8 @@ public: // End the top scope. void EndScope(); private: + friend class RecorderProfiler; + ProfilerNode* CreateNode(); void DestroyNode(ProfilerNode* node); diff --git a/examples/testbed/framework/recorder_profiler.cpp b/examples/testbed/framework/recorder_profiler.cpp index 4edfaed..143ab83 100644 --- a/examples/testbed/framework/recorder_profiler.cpp +++ b/examples/testbed/framework/recorder_profiler.cpp @@ -17,37 +17,10 @@ */ #include +#include RecorderProfiler* g_recorderProfiler = nullptr; -void RecorderProfiler::BeginEvents() -{ - m_call = 0; - for (u32 i = 0; i < m_records.Count(); ++i) - { - m_records[i].elapsed = 0.0; - m_records[i].call = 0; - } -} - -void RecorderProfiler::EndEvents() -{ - for (u32 i = 0; i < m_records.Count(); ++i) - { - ProfilerRecord& r1 = m_records[i]; - - for (u32 j = i + 1; j < m_records.Count(); ++j) - { - ProfilerRecord& r2 = m_records[j]; - - if (r2.call < r1.call) - { - b3Swap(r1, r2); - } - } - } -} - ProfilerRecord* RecorderProfiler::FindRecord(const char* name) { for (u32 i = 0; i < m_records.Count(); ++i) @@ -61,35 +34,87 @@ ProfilerRecord* RecorderProfiler::FindRecord(const char* name) return nullptr; } -void RecorderProfiler::BeginEvent(const char* name, float64 time) +void RecorderProfiler::RecurseBuildRecords(ProfilerNode* node) { - ++m_call; - - ProfilerRecord* fr = FindRecord(name); + ProfilerRecord* fr = FindRecord(node->name); if (fr) { - fr->time = time; - fr->call = m_call; - return; + float64 elapsedTime = node->t1 - node->t0; + + fr->elapsed += elapsedTime; + fr->minElapsed = b3Min(fr->minElapsed, elapsedTime); + fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); + ++fr->callCount; } + else + { + float64 elapsedTime = node->t1 - node->t0; - ProfilerRecord r; - r.name = name; - r.time = time; - r.elapsed = 0.0; - r.maxElapsed = 0.0; - r.call = m_call; + ProfilerRecord r; + r.name = node->name; + r.elapsed = elapsedTime; + r.minElapsed = elapsedTime; + r.maxElapsed = elapsedTime; + r.callCount = 1; - m_records.PushBack(r); + m_records.PushBack(r); + } + + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseBuildRecords(node->children[i]); + } } -void RecorderProfiler::EndEvent(const char* name, float64 time) +void RecorderProfiler::BuildRecords() { - ProfilerRecord* fr = FindRecord(name); - B3_ASSERT(fr != nullptr); + for (u32 i = 0; i < m_records.Count(); ++i) + { + m_records[i].elapsed = 0.0; + m_records[i].callCount = 0; + } - float64 elapsedTime = time - fr->time; + RecurseBuildRecords(g_profiler->m_root); +} - fr->elapsed += elapsedTime; - fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); +static ProfilerRecord* FindSortedRecord(b3Array& records, const char* name) +{ + for (u32 i = 0; i < records.Count(); ++i) + { + ProfilerRecord* r = records[i]; + if (r->name == name) + { + return r; + } + } + return nullptr; +} + +void RecorderProfiler::RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output) +{ + ProfilerRecord* fsr = FindSortedRecord(output, node->name); + + if (fsr == nullptr) + { + ProfilerRecord* fr = FindRecord(node->name); + + assert(fr != nullptr); + + // Push back the first ocurrence of call in calling order + output.PushBack(fr); + } + + for (u32 i = 0; i < node->children.Count(); ++i) + { + RecurseBuildSortedRecords(node->children[i], output); + } +} + +void RecorderProfiler::BuildSortedRecords(b3Array& output) +{ + assert(output.Count() == 0); + + output.Reserve(m_records.Count()); + + RecurseBuildSortedRecords(g_profiler->m_root, output); } \ No newline at end of file diff --git a/examples/testbed/framework/recorder_profiler.h b/examples/testbed/framework/recorder_profiler.h index 3934ae7..9d6eae6 100644 --- a/examples/testbed/framework/recorder_profiler.h +++ b/examples/testbed/framework/recorder_profiler.h @@ -19,39 +19,38 @@ #ifndef RECORDER_PROFILER_H #define RECORDER_PROFILER_H -#include #include +#include -// An event in the profiler event recorder. +struct ProfilerNode; + +// A persistent profiler record struct ProfilerRecord { const char* name; - float64 time; float64 elapsed; + float64 minElapsed; float64 maxElapsed; - u32 call; + u32 callCount; }; -// The profiler recorder simply keeps profile events in an event buffer, -// so that they can be rendered, saved to a file, etc. later in a -// particular position in code. +// This class maintains persistent profiler records class RecorderProfiler { public: - void BeginEvents(); - - void EndEvents(); - - void BeginEvent(const char* name, float64 time); + void BuildRecords(); + + void BuildSortedRecords(b3Array& output); - void EndEvent(const char* name, float64 time); - const b3Array& GetRecords() const { return m_records; } private: - ProfilerRecord* FindRecord(const char* name); + void RecurseBuildRecords(ProfilerNode* node); - b3StackArray m_records; // persistent records sorted by call order - u32 m_call; + void RecurseBuildSortedRecords(ProfilerNode* node, b3Array& output); + + ProfilerRecord* FindRecord(const char* name); + + b3StackArray m_records; // persistent profiler records }; extern RecorderProfiler* g_recorderProfiler; diff --git a/examples/testbed/framework/testbed_profiler.h b/examples/testbed/framework/testbed_profiler.h deleted file mode 100644 index a770937..0000000 --- a/examples/testbed/framework/testbed_profiler.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef TESTBED_PROFILER_H -#define TESTBED_PROFILER_H - -#include -#include - -// Set to 1 then the testbed profiler will write profile events into a .json file. -// Set to 0 otherwise. -#define PROFILE_JSON 0 - -#if (PROFILE_JSON == 1) - #include -#endif - -class TestbedProfiler : public ProfilerListener -{ -public: - void BeginEvents() override - { - m_recorderListener.BeginEvents(); - -#if (PROFILE_JSON == 1) - m_jsonListener.BeginEvents(); -#endif - - } - - void EndEvents() override - { - m_recorderListener.EndEvents(); - -#if (PROFILE_JSON == 1) - m_jsonListener.EndEvents(); -#endif - - } - - void BeginEvent(const char* name, float64 time) override - { - m_recorderListener.BeginEvent(name, time); - -#if (PROFILE_JSON == 1) - m_jsonListener.BeginEvent(name, time); -#endif - } - - void EndEvent(const char* name, float64 time) override - { - m_recorderListener.EndEvent(name, time); - -#if (PROFILE_JSON == 1) - m_jsonListener.EndEvent(name, time); -#endif - } - - RecorderProfiler m_recorderListener; - -#if (PROFILE_JSON == 1) - JsonProfiler m_jsonListener; -#endif - -}; - -#endif \ No newline at end of file