improve profiler

This commit is contained in:
Irlan
2019-04-01 11:03:12 -03:00
parent 3f55504a91
commit f195e77be7
8 changed files with 133 additions and 162 deletions

View File

@ -19,6 +19,8 @@
#ifndef JSON_PROFILER_H #ifndef JSON_PROFILER_H
#define JSON_PROFILER_H #define JSON_PROFILER_H
#include <testbed/framework/profiler.h>
#include <bounce/common/settings.h> #include <bounce/common/settings.h>
#include <rapidjson/filewritestream.h> #include <rapidjson/filewritestream.h>
@ -33,19 +35,19 @@ using namespace rapidjson;
// Say chrome://tracing to the web browser and load the file // Say chrome://tracing to the web browser and load the file
// This file is by default called "profile.json". Any name can be given. // This file is by default called "profile.json". Any name can be given.
// For implementation details, see json_profile.cpp. // For implementation details, see json_profile.cpp.
class JsonProfiler class JsonProfiler : public ProfilerListener
{ {
public: public:
JsonProfiler(); JsonProfiler();
~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: private:
FILE * m_file; FILE * m_file;
FileWriteStream* m_stream; FileWriteStream* m_stream;

View File

@ -101,6 +101,8 @@ static void Run()
glfwGetWindowSize(g_window, &w, &h); glfwGetWindowSize(g_window, &w, &h);
g_view->Event_SetWindowSize(u32(w), u32(h)); g_view->Event_SetWindowSize(u32(w), u32(h));
b3StackArray<ProfilerRecord*, 256> records;
while (glfwWindowShouldClose(g_window) == 0) while (glfwWindowShouldClose(g_window) == 0)
{ {
g_profiler->Begin(); g_profiler->Begin();
@ -120,14 +122,10 @@ static void Run()
if (g_settings->drawProfile) if (g_settings->drawProfile)
{ {
const b3Array<ProfilerRecord>& records = g_recorderProfiler->GetRecords();
for (u32 i = 0; i < records.Count(); ++i) for (u32 i = 0; i < records.Count(); ++i)
{ {
const ProfilerRecord& r = records[i]; ProfilerRecord* r = records[i];
if (r.call != 0) g_draw->DrawString(b3Color_white, "%s %.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", r->name, r->elapsed, r->minElapsed, r->maxElapsed, r->callCount);
{
g_draw->DrawString(b3Color_white, "%s %.4f (%.4f) [ms]", r.name, r.elapsed, r.maxElapsed);
}
} }
} }
@ -138,6 +136,14 @@ static void Run()
g_view->EndInterface(); g_view->EndInterface();
g_profiler->EndScope(); g_profiler->EndScope();
g_recorderProfiler->BuildRecords();
if (g_settings->drawProfile)
{
records.Resize(0);
g_recorderProfiler->BuildSortedRecords(records);
}
g_profiler->End(); g_profiler->End();

View File

@ -26,8 +26,11 @@ Model::Model()
g_draw = &m_draw; g_draw = &m_draw;
g_camera = &m_camera; g_camera = &m_camera;
g_profiler = &m_profiler; g_profiler = &m_profiler;
g_recorderProfiler = &m_profilerListener.m_recorderListener; g_recorderProfiler = &m_recorderProfiler;
g_profilerListener = &m_profilerListener;
#if (PROFILE_JSON == 1)
g_profilerListener = &m_jsonListener;
#endif
m_test = nullptr; m_test = nullptr;
@ -54,7 +57,10 @@ Model::~Model()
g_camera = nullptr; g_camera = nullptr;
g_profiler = nullptr; g_profiler = nullptr;
g_recorderProfiler = nullptr; g_recorderProfiler = nullptr;
#if (PROFILE_JSON == 1)
g_profilerListener = nullptr; g_profilerListener = nullptr;
#endif
delete m_test; delete m_test;
} }

View File

@ -20,7 +20,15 @@
#define MODEL_H #define MODEL_H
#include <testbed/framework/draw.h> #include <testbed/framework/draw.h>
#include <testbed/framework/testbed_profiler.h> #include <testbed/framework/profiler.h>
#include <testbed/framework/recorder_profiler.h>
// Set to 1 to write profile events into a .json file. Set to 0 otherwise.
#define PROFILE_JSON 1
#if (PROFILE_JSON == 1)
#include <testbed\framework\json_profiler.h>
#endif
class Test; class Test;
@ -63,7 +71,12 @@ private:
Draw m_draw; Draw m_draw;
Camera m_camera; Camera m_camera;
Profiler m_profiler; Profiler m_profiler;
TestbedProfiler m_profilerListener; RecorderProfiler m_recorderProfiler;
#if (PROFILE_JSON == 1)
JsonProfiler m_jsonListener;
#endif
Test* m_test; Test* m_test;
bool m_setTest; bool m_setTest;
bool m_pause; bool m_pause;

View File

@ -59,6 +59,8 @@ public:
// End the top scope. // End the top scope.
void EndScope(); void EndScope();
private: private:
friend class RecorderProfiler;
ProfilerNode* CreateNode(); ProfilerNode* CreateNode();
void DestroyNode(ProfilerNode* node); void DestroyNode(ProfilerNode* node);

View File

@ -17,37 +17,10 @@
*/ */
#include <testbed/framework/recorder_profiler.h> #include <testbed/framework/recorder_profiler.h>
#include <testbed/framework/profiler.h>
RecorderProfiler* g_recorderProfiler = nullptr; 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) ProfilerRecord* RecorderProfiler::FindRecord(const char* name)
{ {
for (u32 i = 0; i < m_records.Count(); ++i) for (u32 i = 0; i < m_records.Count(); ++i)
@ -61,35 +34,87 @@ ProfilerRecord* RecorderProfiler::FindRecord(const char* name)
return nullptr; return nullptr;
} }
void RecorderProfiler::BeginEvent(const char* name, float64 time) void RecorderProfiler::RecurseBuildRecords(ProfilerNode* node)
{ {
++m_call; ProfilerRecord* fr = FindRecord(node->name);
ProfilerRecord* fr = FindRecord(name);
if (fr) if (fr)
{ {
fr->time = time; float64 elapsedTime = node->t1 - node->t0;
fr->call = m_call;
return; 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; ProfilerRecord r;
r.name = name; r.name = node->name;
r.time = time; r.elapsed = elapsedTime;
r.elapsed = 0.0; r.minElapsed = elapsedTime;
r.maxElapsed = 0.0; r.maxElapsed = elapsedTime;
r.call = m_call; 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); for (u32 i = 0; i < m_records.Count(); ++i)
B3_ASSERT(fr != nullptr); {
m_records[i].elapsed = 0.0;
m_records[i].callCount = 0;
}
float64 elapsedTime = time - fr->time; RecurseBuildRecords(g_profiler->m_root);
}
fr->elapsed += elapsedTime; static ProfilerRecord* FindSortedRecord(b3Array<ProfilerRecord*>& records, const char* name)
fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime); {
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<ProfilerRecord*>& 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<ProfilerRecord*>& output)
{
assert(output.Count() == 0);
output.Reserve(m_records.Count());
RecurseBuildSortedRecords(g_profiler->m_root, output);
} }

View File

@ -19,39 +19,38 @@
#ifndef RECORDER_PROFILER_H #ifndef RECORDER_PROFILER_H
#define RECORDER_PROFILER_H #define RECORDER_PROFILER_H
#include <bounce/common/template/array.h>
#include <bounce/common/math/math.h> #include <bounce/common/math/math.h>
#include <bounce/common/template/array.h>
// An event in the profiler event recorder. struct ProfilerNode;
// A persistent profiler record
struct ProfilerRecord struct ProfilerRecord
{ {
const char* name; const char* name;
float64 time;
float64 elapsed; float64 elapsed;
float64 minElapsed;
float64 maxElapsed; float64 maxElapsed;
u32 call; u32 callCount;
}; };
// The profiler recorder simply keeps profile events in an event buffer, // This class maintains persistent profiler records
// so that they can be rendered, saved to a file, etc. later in a
// particular position in code.
class RecorderProfiler class RecorderProfiler
{ {
public: public:
void BeginEvents(); void BuildRecords();
void EndEvents(); void BuildSortedRecords(b3Array<ProfilerRecord*>& output);
void BeginEvent(const char* name, float64 time);
void EndEvent(const char* name, float64 time);
const b3Array<ProfilerRecord>& GetRecords() const { return m_records; } const b3Array<ProfilerRecord>& GetRecords() const { return m_records; }
private: private:
ProfilerRecord* FindRecord(const char* name); void RecurseBuildRecords(ProfilerNode* node);
b3StackArray<ProfilerRecord, 256> m_records; // persistent records sorted by call order void RecurseBuildSortedRecords(ProfilerNode* node, b3Array<ProfilerRecord*>& output);
u32 m_call;
ProfilerRecord* FindRecord(const char* name);
b3StackArray<ProfilerRecord, 256> m_records; // persistent profiler records
}; };
extern RecorderProfiler* g_recorderProfiler; extern RecorderProfiler* g_recorderProfiler;

View File

@ -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 <testbed/framework/profiler.h>
#include <testbed/framework/recorder_profiler.h>
// 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 <testbed\framework\json_profiler.h>
#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