mirror of
https://github.com/minetest/minetest.git
synced 2025-03-06 20:48:40 +01:00
Refactor profiler and related classes
This commit is contained in:
parent
5a07f5a652
commit
72eeb9fecb
7 changed files with 133 additions and 132 deletions
|
@ -1132,9 +1132,11 @@ void Game::run()
|
||||||
FpsControl draw_times;
|
FpsControl draw_times;
|
||||||
f32 dtime; // in seconds
|
f32 dtime; // in seconds
|
||||||
|
|
||||||
/* Clear the profiler */
|
// Clear the profiler
|
||||||
Profiler::GraphValues dummyvalues;
|
{
|
||||||
g_profiler->graphGet(dummyvalues);
|
Profiler::GraphValues dummyvalues;
|
||||||
|
g_profiler->graphPop(dummyvalues);
|
||||||
|
}
|
||||||
|
|
||||||
draw_times.reset();
|
draw_times.reset();
|
||||||
|
|
||||||
|
@ -4229,7 +4231,7 @@ void Game::updateClouds(float dtime)
|
||||||
inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
|
inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
|
||||||
{
|
{
|
||||||
Profiler::GraphValues values;
|
Profiler::GraphValues values;
|
||||||
g_profiler->graphGet(values);
|
g_profiler->graphPop(values);
|
||||||
graph->put(values);
|
graph->put(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,14 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
enum TimePrecision
|
|
||||||
{
|
|
||||||
PRECISION_SECONDS,
|
|
||||||
PRECISION_MILLI,
|
|
||||||
PRECISION_MICRO,
|
|
||||||
PRECISION_NANO
|
|
||||||
};
|
|
||||||
|
|
||||||
inline struct tm mt_localtime()
|
inline struct tm mt_localtime()
|
||||||
{
|
{
|
||||||
// initialize the time zone on first invocation
|
// initialize the time zone on first invocation
|
||||||
|
|
|
@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "irrlichttypes.h" // u64
|
#include "irrlichttypes.h" // u64
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "gettime.h"
|
#include "util/timetaker.h" // TimePrecision
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define SWPRINTF_CHARSTRING L"%S"
|
#define SWPRINTF_CHARSTRING L"%S"
|
||||||
|
|
145
src/profiler.cpp
145
src/profiler.cpp
|
@ -22,40 +22,37 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
static Profiler main_profiler;
|
static Profiler main_profiler;
|
||||||
Profiler *g_profiler = &main_profiler;
|
Profiler *g_profiler = &main_profiler;
|
||||||
ScopeProfiler::ScopeProfiler(
|
|
||||||
Profiler *profiler, const std::string &name, ScopeProfilerType type) :
|
ScopeProfiler::ScopeProfiler(Profiler *profiler, const std::string &name,
|
||||||
m_profiler(profiler),
|
ScopeProfilerType type, TimePrecision prec) :
|
||||||
m_name(name), m_type(type)
|
m_profiler(profiler),
|
||||||
|
m_name(name), m_type(type), m_precision(prec)
|
||||||
{
|
{
|
||||||
m_name.append(" [ms]");
|
m_name.append(" [").append(TimePrecision_units[prec]).append("]");
|
||||||
if (m_profiler)
|
m_time1 = porting::getTime(prec);
|
||||||
m_timer = new TimeTaker(m_name, nullptr, PRECISION_MILLI);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeProfiler::~ScopeProfiler()
|
ScopeProfiler::~ScopeProfiler()
|
||||||
{
|
{
|
||||||
if (!m_timer)
|
if (!m_profiler)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float duration_ms = m_timer->stop(true);
|
float duration = porting::getTime(m_precision) - m_time1;
|
||||||
float duration = duration_ms;
|
|
||||||
if (m_profiler) {
|
switch (m_type) {
|
||||||
switch (m_type) {
|
case SPT_ADD:
|
||||||
case SPT_ADD:
|
m_profiler->add(m_name, duration);
|
||||||
m_profiler->add(m_name, duration);
|
break;
|
||||||
break;
|
case SPT_AVG:
|
||||||
case SPT_AVG:
|
m_profiler->avg(m_name, duration);
|
||||||
m_profiler->avg(m_name, duration);
|
break;
|
||||||
break;
|
case SPT_GRAPH_ADD:
|
||||||
case SPT_GRAPH_ADD:
|
m_profiler->graphAdd(m_name, duration);
|
||||||
m_profiler->graphAdd(m_name, duration);
|
break;
|
||||||
break;
|
case SPT_MAX:
|
||||||
case SPT_MAX:
|
m_profiler->max(m_name, duration);
|
||||||
m_profiler->max(m_name, duration);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete m_timer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Profiler::Profiler()
|
Profiler::Profiler()
|
||||||
|
@ -66,92 +63,68 @@ Profiler::Profiler()
|
||||||
void Profiler::add(const std::string &name, float value)
|
void Profiler::add(const std::string &name, float value)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
{
|
|
||||||
/* No average shall have been used; mark add/max used as -2 */
|
auto it = m_data.find(name);
|
||||||
std::map<std::string, int>::iterator n = m_avgcounts.find(name);
|
if (it == m_data.end()) {
|
||||||
if (n == m_avgcounts.end()) {
|
// mark with special value for checking
|
||||||
m_avgcounts[name] = -2;
|
m_data.emplace(name, DataPair{value, -SPT_ADD});
|
||||||
} else {
|
} else {
|
||||||
if (n->second == -1)
|
assert(it->second.avgcount == -SPT_ADD);
|
||||||
n->second = -2;
|
it->second.value += value;
|
||||||
assert(n->second == -2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::map<std::string, float>::iterator n = m_data.find(name);
|
|
||||||
if (n == m_data.end())
|
|
||||||
m_data[name] = value;
|
|
||||||
else
|
|
||||||
n->second += value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profiler::max(const std::string &name, float value)
|
void Profiler::max(const std::string &name, float value)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
{
|
|
||||||
/* No average shall have been used; mark add/max used as -2 */
|
auto it = m_data.find(name);
|
||||||
auto n = m_avgcounts.find(name);
|
if (it == m_data.end()) {
|
||||||
if (n == m_avgcounts.end()) {
|
// mark with special value for checking
|
||||||
m_avgcounts[name] = -2;
|
m_data.emplace(name, DataPair{value, -SPT_MAX});
|
||||||
} else {
|
} else {
|
||||||
if (n->second == -1)
|
assert(it->second.avgcount == -SPT_MAX);
|
||||||
n->second = -2;
|
it->second.value = std::max(value, it->second.value);
|
||||||
assert(n->second == -2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto n = m_data.find(name);
|
|
||||||
if (n == m_data.end())
|
|
||||||
m_data[name] = value;
|
|
||||||
else if (value > n->second)
|
|
||||||
n->second = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profiler::avg(const std::string &name, float value)
|
void Profiler::avg(const std::string &name, float value)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
int &count = m_avgcounts[name];
|
|
||||||
|
|
||||||
assert(count != -2);
|
auto it = m_data.find(name);
|
||||||
count = MYMAX(count, 0) + 1;
|
if (it == m_data.end()) {
|
||||||
m_data[name] += value;
|
m_data.emplace(name, DataPair{value, 1});
|
||||||
|
} else {
|
||||||
|
assert(it->second.avgcount >= 1);
|
||||||
|
it->second.value += value;
|
||||||
|
it->second.avgcount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profiler::clear()
|
void Profiler::clear()
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
for (auto &it : m_data) {
|
for (auto &it : m_data)
|
||||||
it.second = 0;
|
it.second = DataPair();
|
||||||
}
|
|
||||||
m_avgcounts.clear();
|
|
||||||
m_start_time = porting::getTimeMs();
|
m_start_time = porting::getTimeMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Profiler::getValue(const std::string &name) const
|
float Profiler::getValue(const std::string &name) const
|
||||||
{
|
{
|
||||||
auto numerator = m_data.find(name);
|
auto it = m_data.find(name);
|
||||||
if (numerator == m_data.end())
|
if (it == m_data.end())
|
||||||
return 0.f;
|
return 0;
|
||||||
|
return it->second.getValue();
|
||||||
auto denominator = m_avgcounts.find(name);
|
|
||||||
if (denominator != m_avgcounts.end()) {
|
|
||||||
if (denominator->second >= 1)
|
|
||||||
return numerator->second / denominator->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return numerator->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Profiler::getAvgCount(const std::string &name) const
|
int Profiler::getAvgCount(const std::string &name) const
|
||||||
{
|
{
|
||||||
auto n = m_avgcounts.find(name);
|
auto it = m_data.find(name);
|
||||||
|
if (it == m_data.end())
|
||||||
if (n != m_avgcounts.end() && n->second >= 1)
|
return 1;
|
||||||
return n->second;
|
int denominator = it->second.avgcount;
|
||||||
|
return denominator >= 1 ? denominator : 1;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Profiler::getElapsedMs() const
|
u64 Profiler::getElapsedMs() const
|
||||||
|
@ -204,6 +177,6 @@ void Profiler::getPage(GraphValues &o, u32 page, u32 pagecount)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
o[i.first] = i.second / getAvgCount(i.first);
|
o[i.first] = i.second.getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,54 +58,71 @@ public:
|
||||||
void getPage(GraphValues &o, u32 page, u32 pagecount);
|
void getPage(GraphValues &o, u32 page, u32 pagecount);
|
||||||
|
|
||||||
|
|
||||||
|
void graphSet(const std::string &id, float value)
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(m_mutex);
|
||||||
|
m_graphvalues[id] = value;
|
||||||
|
}
|
||||||
void graphAdd(const std::string &id, float value)
|
void graphAdd(const std::string &id, float value)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
std::map<std::string, float>::iterator i =
|
auto it = m_graphvalues.find(id);
|
||||||
m_graphvalues.find(id);
|
if (it == m_graphvalues.end())
|
||||||
if(i == m_graphvalues.end())
|
m_graphvalues.emplace(id, value);
|
||||||
m_graphvalues[id] = value;
|
|
||||||
else
|
else
|
||||||
i->second += value;
|
it->second += value;
|
||||||
}
|
}
|
||||||
void graphGet(GraphValues &result)
|
void graphPop(GraphValues &result)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
result = m_graphvalues;
|
assert(result.empty());
|
||||||
m_graphvalues.clear();
|
std::swap(result, m_graphvalues);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(const std::string& name)
|
void remove(const std::string& name)
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(m_mutex);
|
MutexAutoLock lock(m_mutex);
|
||||||
m_avgcounts.erase(name);
|
|
||||||
m_data.erase(name);
|
m_data.erase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct DataPair {
|
||||||
|
float value = 0;
|
||||||
|
int avgcount = 0;
|
||||||
|
|
||||||
|
inline float getValue() const {
|
||||||
|
return avgcount >= 1 ? (value / avgcount) : value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::map<std::string, float> m_data;
|
std::map<std::string, DataPair> m_data;
|
||||||
std::map<std::string, int> m_avgcounts;
|
|
||||||
std::map<std::string, float> m_graphvalues;
|
std::map<std::string, float> m_graphvalues;
|
||||||
u64 m_start_time;
|
u64 m_start_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ScopeProfilerType{
|
enum ScopeProfilerType : u8
|
||||||
SPT_ADD,
|
{
|
||||||
|
SPT_ADD = 1,
|
||||||
SPT_AVG,
|
SPT_AVG,
|
||||||
SPT_GRAPH_ADD,
|
SPT_GRAPH_ADD,
|
||||||
SPT_MAX
|
SPT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Note: this class should be kept lightweight.
|
||||||
|
|
||||||
class ScopeProfiler
|
class ScopeProfiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ScopeProfiler(Profiler *profiler, const std::string &name,
|
ScopeProfiler(Profiler *profiler, const std::string &name,
|
||||||
ScopeProfilerType type = SPT_ADD);
|
ScopeProfilerType type = SPT_ADD,
|
||||||
|
TimePrecision precision = PRECISION_MILLI);
|
||||||
~ScopeProfiler();
|
~ScopeProfiler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Profiler *m_profiler = nullptr;
|
Profiler *m_profiler = nullptr;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
TimeTaker *m_timer = nullptr;
|
u64 m_time1;
|
||||||
enum ScopeProfilerType m_type;
|
ScopeProfilerType m_type;
|
||||||
|
TimePrecision m_precision;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,12 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
TimeTaker::TimeTaker(const std::string &name, u64 *result, TimePrecision prec)
|
void TimeTaker::start()
|
||||||
{
|
{
|
||||||
m_name = name;
|
m_time1 = porting::getTime(m_precision);
|
||||||
m_result = result;
|
|
||||||
m_precision = prec;
|
|
||||||
m_time1 = porting::getTime(prec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 TimeTaker::stop(bool quiet)
|
u64 TimeTaker::stop(bool quiet)
|
||||||
|
@ -39,15 +36,8 @@ u64 TimeTaker::stop(bool quiet)
|
||||||
(*m_result) += dtime;
|
(*m_result) += dtime;
|
||||||
} else {
|
} else {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
static const char* const units[] = {
|
|
||||||
"s" /* PRECISION_SECONDS */,
|
|
||||||
"ms" /* PRECISION_MILLI */,
|
|
||||||
"us" /* PRECISION_MICRO */,
|
|
||||||
"ns" /* PRECISION_NANO */,
|
|
||||||
};
|
|
||||||
infostream << m_name << " took "
|
infostream << m_name << " took "
|
||||||
<< dtime << units[m_precision]
|
<< dtime << TimePrecision_units[m_precision] << std::endl;
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_running = false;
|
m_running = false;
|
||||||
|
|
|
@ -19,18 +19,43 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include "irrlichttypes.h"
|
#include "irrlichttypes.h"
|
||||||
#include "gettime.h"
|
|
||||||
|
enum TimePrecision : s8
|
||||||
|
{
|
||||||
|
PRECISION_SECONDS,
|
||||||
|
PRECISION_MILLI,
|
||||||
|
PRECISION_MICRO,
|
||||||
|
PRECISION_NANO,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const char *TimePrecision_units[] = {
|
||||||
|
"s" /* PRECISION_SECONDS */,
|
||||||
|
"ms" /* PRECISION_MILLI */,
|
||||||
|
"us" /* PRECISION_MICRO */,
|
||||||
|
"ns" /* PRECISION_NANO */,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TimeTaker
|
TimeTaker
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Note: this class should be kept lightweight
|
||||||
|
|
||||||
class TimeTaker
|
class TimeTaker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TimeTaker(const std::string &name, u64 *result=nullptr,
|
TimeTaker(const std::string &name, u64 *result = nullptr,
|
||||||
TimePrecision prec=PRECISION_MILLI);
|
TimePrecision prec = PRECISION_MILLI)
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
m_result = result;
|
||||||
|
else
|
||||||
|
m_name = name;
|
||||||
|
m_precision = prec;
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
~TimeTaker()
|
~TimeTaker()
|
||||||
{
|
{
|
||||||
|
@ -42,9 +67,11 @@ public:
|
||||||
u64 getTimerTime();
|
u64 getTimerTime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void start();
|
||||||
|
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
u64 *m_result = nullptr;
|
||||||
u64 m_time1;
|
u64 m_time1;
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
TimePrecision m_precision;
|
TimePrecision m_precision;
|
||||||
u64 *m_result = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue