improve profiler
This commit is contained in:
@ -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;
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
@ -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;
|
||||||
|
@ -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
|
|
Reference in New Issue
Block a user