add min, max, stddev to stats

This commit is contained in:
Lion Kortlepel 2024-01-24 01:26:16 +01:00 committed by Lion
parent 88721d4f7f
commit 4347cb4af2
3 changed files with 52 additions and 17 deletions

View File

@ -16,6 +16,13 @@ TimePoint now();
/// Returns a sub-millisecond resolution duration between start and end. /// Returns a sub-millisecond resolution duration between start and end.
Duration duration(const TimePoint& start, const TimePoint& end); Duration duration(const TimePoint& start, const TimePoint& end);
struct Stats {
double mean;
double stddev;
double min;
double max;
};
/// Calculates and stores the moving average over K samples of execution time data /// Calculates and stores the moving average over K samples of execution time data
/// for some single unit of code. Threadsafe. /// for some single unit of code. Threadsafe.
struct UnitExecutionTime { struct UnitExecutionTime {
@ -24,8 +31,9 @@ struct UnitExecutionTime {
/// Adds a sample to the collection, overriding the oldest sample if needed. /// Adds a sample to the collection, overriding the oldest sample if needed.
void add_sample(const Duration& dur); void add_sample(const Duration& dur);
/// Calculates the average duration over the `measurement_count()` measurements. /// Calculates the mean duration over the `measurement_count()` measurements,
Duration average_duration() const; /// as well as the standard deviation.
Stats stats() const;
/// Returns the number of elements the moving average is calculated over. /// Returns the number of elements the moving average is calculated over.
size_t measurement_count(); size_t measurement_count();
@ -39,14 +47,15 @@ struct UnitProfileCollection {
/// Adds a sample to the collection, overriding the oldest sample if needed. /// Adds a sample to the collection, overriding the oldest sample if needed.
void add_sample(const std::string& unit, const Duration& duration); void add_sample(const std::string& unit, const Duration& duration);
/// Calculates the average duration over the `measurement_count()` measurements. /// Calculates the mean duration over the `measurement_count()` measurements,
Duration average_duration(const std::string& unit); /// as well as the standard deviation.
Stats stats(const std::string& unit);
/// Returns the number of elements the moving average is calculated over. /// Returns the number of elements the moving average is calculated over.
size_t measurement_count(const std::string& unit); size_t measurement_count(const std::string& unit);
/// Returns the averages for all stored units. /// Returns the stats for all stored units.
std::unordered_map<std::string, double> all_average_durations(); std::unordered_map<std::string, Stats> all_stats();
private: private:
boost::synchronized_value<std::unordered_map<std::string, UnitExecutionTime>> m_map; boost::synchronized_value<std::unordered_map<std::string, UnitExecutionTime>> m_map;

View File

@ -1,4 +1,5 @@
#include "Profiling.h" #include "Profiling.h"
#include <limits>
prof::Duration prof::duration(const TimePoint& start, const TimePoint& end) { prof::Duration prof::duration(const TimePoint& start, const TimePoint& end) {
return end - start; return end - start;
@ -6,8 +7,8 @@ prof::Duration prof::duration(const TimePoint& start, const TimePoint& end) {
prof::TimePoint prof::now() { prof::TimePoint prof::now() {
return std::chrono::high_resolution_clock::now(); return std::chrono::high_resolution_clock::now();
} }
prof::Duration prof::UnitProfileCollection::average_duration(const std::string& unit) { prof::Stats prof::UnitProfileCollection::stats(const std::string& unit) {
return m_map->operator[](unit).average_duration(); return m_map->operator[](unit).stats();
} }
size_t prof::UnitProfileCollection::measurement_count(const std::string& unit) { size_t prof::UnitProfileCollection::measurement_count(const std::string& unit) {
@ -22,13 +23,35 @@ size_t prof::UnitExecutionTime::measurement_count() {
return m_measurements->size(); return m_measurements->size();
} }
prof::Duration prof::UnitExecutionTime::average_duration() const { prof::Stats prof::UnitExecutionTime::stats() const {
Stats result {};
// calculate sum
auto measurements = m_measurements.synchronize(); auto measurements = m_measurements.synchronize();
if (measurements->size() == 0) {
return result;
}
result.max = std::numeric_limits<double>::min();
result.min = std::numeric_limits<double>::max();
Duration sum {}; Duration sum {};
for (const auto& measurement : *measurements) { for (const auto& measurement : *measurements) {
if (measurement.count() > result.max) {
result.max = measurement.count();
}
if (measurement.count() < result.min) {
result.min = measurement.count();
}
sum += measurement; sum += measurement;
} }
return sum / measurements->size(); // calculate mean
result.mean = (sum / measurements->size()).count();
// calculate stddev
result.stddev = 0;
for (const auto& measurement : *measurements) {
// (measurements[i] - mean)^2
result.stddev += std::pow(measurement.count() - result.mean, 2);
}
result.stddev = std::sqrt(result.stddev / double(measurements->size()));
return result;
} }
void prof::UnitExecutionTime::add_sample(const Duration& dur) { void prof::UnitExecutionTime::add_sample(const Duration& dur) {
@ -39,12 +62,11 @@ prof::UnitExecutionTime::UnitExecutionTime()
: m_measurements(boost::circular_buffer<Duration>(100)) { : m_measurements(boost::circular_buffer<Duration>(100)) {
} }
std::unordered_map<std::string, double> prof::UnitProfileCollection::all_average_durations() { std::unordered_map<std::string, prof::Stats> prof::UnitProfileCollection::all_stats() {
auto map = m_map.synchronize(); auto map = m_map.synchronize();
std::unordered_map<std::string, double> result {}; std::unordered_map<std::string, Stats> result {};
for (const auto& [name, time] : *map) { for (const auto& [name, time] : *map) {
result[name] = time.average_duration().count(); result[name] = time.stats();
} }
return result; return result;
} }

View File

@ -852,9 +852,13 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
UtilTable.set_function("DebugExecutionTime", [this]() -> sol::table { UtilTable.set_function("DebugExecutionTime", [this]() -> sol::table {
sol::state_view StateView(mState); sol::state_view StateView(mState);
sol::table Result = StateView.create_table(); sol::table Result = StateView.create_table();
auto durs = mProfile.all_average_durations(); auto stats = mProfile.all_stats();
for (const auto& [name, dur] : durs) { for (const auto& [name, stat] : stats) {
Result[name] = dur; Result[name] = StateView.create_table();
Result[name]["mean"] = stat.mean;
Result[name]["stddev"] = stat.stddev;
Result[name]["min"] = stat.min;
Result[name]["max"] = stat.max;
} }
return Result; return Result;
}); });