222 lines
3.6 KiB
C++
222 lines
3.6 KiB
C++
|
/*
|
||
|
* Profiler.cpp
|
||
|
* ------------
|
||
|
* Purpose: Performance measuring
|
||
|
* Notes : (currently none)
|
||
|
* Authors: OpenMPT Devs
|
||
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "Profiler.h"
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_BEGIN
|
||
|
|
||
|
|
||
|
#ifdef USE_PROFILER
|
||
|
|
||
|
|
||
|
class Statistics
|
||
|
{
|
||
|
public:
|
||
|
Profile &profile;
|
||
|
Profile::Data data;
|
||
|
double usage;
|
||
|
Statistics(Profile &p) : profile(p)
|
||
|
{
|
||
|
usage = 0.0;
|
||
|
Update();
|
||
|
}
|
||
|
void Update()
|
||
|
{
|
||
|
data = profile.GetAndResetData();
|
||
|
uint64 now = profile.GetTime();
|
||
|
uint64 timewindow = now - data.Start;
|
||
|
if(data.Calls > 0 && timewindow > 0)
|
||
|
{
|
||
|
usage = (double)data.Sum / (double)timewindow;
|
||
|
} else
|
||
|
{
|
||
|
usage = 0.0;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
struct ProfileBlock
|
||
|
{
|
||
|
class Profile * profile;
|
||
|
const char * name;
|
||
|
class Statistics * stats;
|
||
|
};
|
||
|
|
||
|
static constexpr std::size_t MAX_PROFILES = 1024;
|
||
|
|
||
|
static ProfileBlock Profiles[ MAX_PROFILES ];
|
||
|
|
||
|
static std::size_t NextProfile = 0;
|
||
|
|
||
|
|
||
|
static void RegisterProfile(Profile *newprofile)
|
||
|
{
|
||
|
if(NextProfile < MAX_PROFILES)
|
||
|
{
|
||
|
Profiles[NextProfile].profile = newprofile;
|
||
|
Profiles[NextProfile].stats = 0;
|
||
|
NextProfile++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void UnregisterProfile(Profile *oldprofile)
|
||
|
{
|
||
|
for(std::size_t i=0; i<NextProfile; i++) {
|
||
|
if(Profiles[i].profile == oldprofile) {
|
||
|
Profiles[i].profile = 0;
|
||
|
delete Profiles[i].stats;
|
||
|
Profiles[i].stats = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Profiler::Update()
|
||
|
{
|
||
|
for(std::size_t i=0; i<NextProfile; i++)
|
||
|
{
|
||
|
if(!Profiles[i].stats)
|
||
|
{
|
||
|
Profiles[i].stats = new Statistics(*Profiles[i].profile);
|
||
|
} else
|
||
|
{
|
||
|
Profiles[i].stats->Update();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
std::string Profiler::DumpProfiles()
|
||
|
{
|
||
|
std::string ret;
|
||
|
for(std::size_t i=0; i<NextProfile; i++)
|
||
|
{
|
||
|
if(Profiles[i].stats)
|
||
|
{
|
||
|
Statistics &stats = *Profiles[i].stats;
|
||
|
std::string cat;
|
||
|
switch(stats.profile.Category)
|
||
|
{
|
||
|
case Profiler::GUI: cat = "GUI"; break;
|
||
|
case Profiler::Audio: cat = "Audio"; break;
|
||
|
case Profiler::Notify: cat = "Notify"; break;
|
||
|
}
|
||
|
ret += cat + " " + std::string(stats.profile.Name) + ": " + mpt::afmt::right(6, mpt::afmt::fix(stats.usage * 100.0, 3)) + "%\r\n";
|
||
|
}
|
||
|
}
|
||
|
ret += "\r\n";
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
std::vector<double> Profiler::DumpCategories()
|
||
|
{
|
||
|
std::vector<double> ret;
|
||
|
ret.resize(Profiler::CategoriesCount);
|
||
|
for(std::size_t i=0; i<NextProfile; i++)
|
||
|
{
|
||
|
if(Profiles[i].stats)
|
||
|
{
|
||
|
ret[Profiles[i].profile->Category] += Profiles[i].stats->usage;
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
uint64 Profile::GetTime() const
|
||
|
{
|
||
|
LARGE_INTEGER ret;
|
||
|
ret.QuadPart = 0;
|
||
|
QueryPerformanceCounter(&ret);
|
||
|
return ret.QuadPart;
|
||
|
}
|
||
|
|
||
|
|
||
|
uint64 Profile::GetFrequency() const
|
||
|
{
|
||
|
LARGE_INTEGER ret;
|
||
|
ret.QuadPart = 0;
|
||
|
QueryPerformanceFrequency(&ret);
|
||
|
return ret.QuadPart;
|
||
|
}
|
||
|
|
||
|
|
||
|
Profile::Profile(Profiler::Category category, const char *name) : Category(category), Name(name)
|
||
|
{
|
||
|
data.Calls = 0;
|
||
|
data.Sum = 0;
|
||
|
data.Overhead = 0;
|
||
|
data.Start = GetTime();
|
||
|
EnterTime = 0;
|
||
|
RegisterProfile(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
Profile::~Profile()
|
||
|
{
|
||
|
UnregisterProfile(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
Profile::Data Profile::GetAndResetData()
|
||
|
{
|
||
|
Profile::Data ret;
|
||
|
datamutex.lock();
|
||
|
ret = data;
|
||
|
data.Calls = 0;
|
||
|
data.Sum = 0;
|
||
|
data.Overhead = 0;
|
||
|
data.Start = GetTime();
|
||
|
datamutex.unlock();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Profile::Reset()
|
||
|
{
|
||
|
datamutex.lock();
|
||
|
data.Calls = 0;
|
||
|
data.Sum = 0;
|
||
|
data.Overhead = 0;
|
||
|
data.Start = GetTime();
|
||
|
datamutex.unlock();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Profile::Enter()
|
||
|
{
|
||
|
EnterTime = GetTime();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Profile::Leave()
|
||
|
{
|
||
|
uint64 LeaveTime = GetTime();
|
||
|
datamutex.lock();
|
||
|
data.Calls += 1;
|
||
|
data.Sum += LeaveTime - EnterTime;
|
||
|
datamutex.unlock();
|
||
|
}
|
||
|
|
||
|
|
||
|
#else // !USE_PROFILER
|
||
|
|
||
|
MPT_MSVC_WORKAROUND_LNK4221(Profiler)
|
||
|
|
||
|
#endif // USE_PROFILER
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_END
|