cog/Frameworks/OpenMPT.old/OpenMPT/common/Profiler.cpp

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::fmt::right(6, mpt::fmt::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