refactor profiler

code is now much more clean
	-removed ugly nested loops
	-used functions to reduce code clutter and duplcation
removed redundant functions
cleaned up sort and improved names
v1
Brett 2023-03-04 11:42:24 -05:00
parent b6cf11505b
commit 811cdff8d1
5 changed files with 247 additions and 139 deletions

View File

@ -12,9 +12,10 @@
#include <vector>
#include <unordered_map>
#include <blt/std/logging.h>
#include <fstream>
/**
* Defines several disableable macros (#define BLT_DISABLE_PROFILING). If you do not use these macros profiling will not be disableable
* Defines several disableable macros (#define BLT_DISABLE_PROFILING). If you do not use these macros profiling cannot be disabled!
*/
namespace blt::profiling {
@ -42,18 +43,18 @@ namespace blt::profiling {
capture_interval getInterval(const std::string& profileName, const std::string& intervalName);
std::vector<capture_interval> getAllIntervals(const std::string& profileName, const std::string& intervalName);
std::vector<capture_interval> getAllIntervals(
const std::string& profileName, const std::string& intervalName
);
profile getProfile(const std::string& profileName);
void printProfile(
const std::string& profileName, blt::logging::LOG_LEVEL loggingLevel = logging::NONE, bool averageHistory = false,
bool ignoreNegatives = true
const std::string& profileName, logging::LOG_LEVEL loggingLevel = logging::NONE,
bool averageHistory = false, bool ignoreNegatives = true
);
void printOrderedProfile(
const std::string& profileName, logging::LOG_LEVEL loggingLevel = logging::NONE, bool averageHistory = false, bool ignoreNegatives = true
);
void writeProfile(std::ofstream& out, const std::string& profileName);
void discardProfiles();
@ -66,7 +67,8 @@ namespace blt::profiling {
std::string m_Profile;
std::string m_Interval;
public:
scoped_interval(std::string profile, std::string interval): m_Profile(std::move(profile)), m_Interval(std::move(interval)) {
scoped_interval(std::string profile, std::string interval):
m_Profile(std::move(profile)), m_Interval(std::move(interval)) {
blt::profiling::startInterval(m_Profile, m_Interval);
}
@ -82,15 +84,31 @@ namespace blt::profiling {
#define BLT_START_INTERVAL(profileName, intervalName)
#define BLT_END_INTERVAL(profileName, intervalName)
#define BLT_POINT(profileName, pointName)
#define BLT_PRINT_PROFILE(profileName, ...)
#define BLT_PRINT_ORDERED(profileName, ...)
#define BLT_WRITE_ORDERED(profileName, ...)
#else
/**
* Starts an interval to be measured, when ended the row will be added to the specified profile.
*/
#define BLT_START_INTERVAL(profileName, intervalName) blt::profiling::startInterval(profileName, intervalName)
/**
* Ends an interval, adds the interval to the profile.
*/
#define BLT_END_INTERVAL(profileName, intervalName) blt::profiling::endInterval(profileName, intervalName)
/**
* Measures this exact point in time.
*/
#define BLT_POINT(profileName, pointName) blt::profiling::point(profileName, pointName)
/**
* Prints the profile order from least time to most time.
* @param profileName the profile to print
* @param loggingLevel blt::logging::LOG_LEVEL to log with (default: NONE)
* @param averageHistory use the historical collection of interval rows in an average or just the latest? (default: false)
* @param ignoreNegatives ignore negative time values (default: true)
*/
#define BLT_PRINT_PROFILE(profileName, ...) blt::profiling::printProfile(profileName, ##__VA_ARGS__)
#define BLT_PRINT_ORDERED(profileName, ...) blt::profiling::printOrderedProfile(profileName, ##__VA_ARGS__)
/**
* writes the profile to an output stream, ordered from least time to most time, in CSV format.
*/
#define BLT_WRITE_PROFILE(stream, profileName) blt::profiling::writeProfile(stream, profileName, true)
#endif
#define BLT_INTERVAL_START(profileName, intervalName) BLT_START_INTERVAL(profileName, intervalName)
#define BLT_INTERVAL_END(profileName, intervalName) BLT_END_INTERVAL(profileName, intervalName)

View File

@ -18,6 +18,109 @@ namespace blt::profiling {
std::mutex profileLock{};
std::unordered_map<std::string, profile> profiles;
struct IntervalComparable {
long difference;
std::string name;
IntervalComparable(long difference, std::string name):
difference(difference), name(std::move(name)) {}
};
inline void print(const std::vector<std::string>& lines, logging::LOG_LEVEL level) {
auto& logger = logging::getLoggerFromLevel(level);
for (const auto& line : lines)
logger << line;
}
/**
* Copy-sorts the unordered vector into the ordered vector, min -> max
*/
inline void orderIntervals(
const std::unordered_map<std::string, capture_interval>& unordered,
std::vector<IntervalComparable>& ordered
) {
// copy
for (const auto& i : unordered)
ordered.emplace_back(i.second.end - i.second.start, i.first);
// sort
std::sort(
ordered.begin(), ordered.end(),
[&](const IntervalComparable& c1, const IntervalComparable& c2) -> bool {
return c1.difference < c2.difference;
}
);
}
inline void averageIntervals(
const std::unordered_map<std::string, std::vector<capture_interval>>& intervals,
std::unordered_map<std::string, capture_interval>& averagedIntervals
) {
for (const auto& i : intervals) {
const auto& name = i.first;
const auto& interval_vec = i.second;
long total_difference = 0;
for (const auto& value : interval_vec)
total_difference += value.end - value.start;
total_difference /= (long)interval_vec.size();
std::string new_name = "(";
new_name += std::to_string(interval_vec.size());
new_name += ") ";
new_name += name;
// can exploit how the order func works by supplying the difference into end,
// which sorts correctly despite not being a true interval.
averagedIntervals.insert({new_name, capture_interval{0, total_difference}});
}
}
void writeProfile(std::ofstream& out, const std::string& profileName, bool ordered) {
}
void printProfile(
const std::string& profileName, logging::LOG_LEVEL loggingLevel, bool averageHistory,
bool ignoreNegatives
) {
auto& profile = profiles[profileName];
const auto& intervals = profile.intervals;
const auto& points = profile.points;
std::vector<IntervalComparable> ordered_vector;
std::unordered_map<std::string, capture_interval> averaged_intervals;
if (averageHistory)
averageIntervals(profile.historicalIntervals, averaged_intervals);
orderIntervals(averageHistory ? averaged_intervals : intervals, ordered_vector);
string::TableFormatter formatter{profileName};
formatter.addColumn({"Order"});
formatter.addColumn({"Interval"});
formatter.addColumn({"Time (ns)"});
formatter.addColumn({"Time (ms)"});
int index = 1;
for (const auto& interval : ordered_vector) {
formatter.addRow(
{std::to_string(index++), interval.name, std::to_string(interval.difference),
std::to_string((double)interval.difference / 1000000.0)}
);
}
std::vector<std::string> updatedLines;
const auto& lines = formatter.createTable(true, true);
for (const auto& line : lines)
updatedLines.emplace_back(line + "\n");
print(updatedLines, loggingLevel);
}
// --- small helper functions below ---
void startInterval(const std::string& profileName, const std::string& intervalName) {
std::scoped_lock lock(profileLock);
capture_interval interval{};
@ -28,7 +131,9 @@ namespace blt::profiling {
void endInterval(const std::string& profileName, const std::string& intervalName) {
std::scoped_lock lock(profileLock);
profiles[profileName].intervals[intervalName].end = system::getCurrentTimeNanoseconds();
profiles[profileName].historicalIntervals[intervalName].push_back(profiles[profileName].intervals[intervalName]);
profiles[profileName].historicalIntervals[intervalName].push_back(
profiles[profileName].intervals[intervalName]
);
}
void point(const std::string& profileName, const std::string& pointName) {
@ -47,121 +152,6 @@ namespace blt::profiling {
return profiles[profileName];
}
inline void print(const std::vector<std::string>& lines, logging::LOG_LEVEL level) {
auto& logger = logging::getLoggerFromLevel(level);
for (const auto& line : lines)
logger << line;
}
void printProfile(const std::string& profileName, blt::logging::LOG_LEVEL loggingLevel, bool averageHistory, bool ignoreNegatives) {
string::TableFormatter formatter{profileName};
formatter.addColumn({"Interval"});
formatter.addColumn({"Time (ns)"});
formatter.addColumn({"Time (ms)"});
auto& profile = profiles[profileName];
const auto& intervals = profile.intervals;
const auto& points = profile.points;
for (const auto& interval : intervals) {
if (averageHistory) {
const auto& history = profile.historicalIntervals[interval.first];
long total_difference = 0;
for (const auto& h_interval : history) {
const auto difference = h_interval.end - h_interval.start;
if (ignoreNegatives && difference < 0)
continue;
total_difference += difference;
}
total_difference /= (long) history.size();
std::string name = "(";
name += std::to_string(history.size());
name += ") ";
name += interval.first;
formatter.addRow({name, std::to_string(total_difference), std::to_string((double) total_difference / 1000000.0)});
} else {
const auto difference = interval.second.end - interval.second.start;
if (ignoreNegatives && difference < 0)
continue;
formatter.addRow({interval.first, std::to_string(difference), std::to_string((double) difference / 1000000.0)});
}
}
std::vector<std::string> updatedLines;
const auto& lines = formatter.createTable(true, true);
for (const auto& line : lines)
updatedLines.emplace_back(line + "\n");
print(updatedLines, loggingLevel);
}
struct timeOrderContainer {
long difference;
std::string name;
timeOrderContainer(long difference, std::string name): difference(difference), name(std::move(name)) {}
};
inline bool timeCompare(const timeOrderContainer& container1, const timeOrderContainer& container2) {
return container1.difference < container2.difference;
}
void printOrderedProfile(const std::string& profileName, logging::LOG_LEVEL loggingLevel, bool averageHistory, bool ignoreNegatives) {
auto& profile = profiles[profileName];
const auto& intervals = profile.intervals;
const auto& points = profile.points;
std::vector<timeOrderContainer> unorderedIntervalVector;
// TODO: refactor to reduce nesting
for (const auto& interval : intervals) {
if (averageHistory) {
const auto& history = profile.historicalIntervals[interval.first];
long total_difference = 0;
for (const auto& h_interval : history) {
const auto difference = h_interval.end - h_interval.start;
if (ignoreNegatives && difference < 0)
continue;
total_difference += difference;
}
total_difference /= (long) history.size();
std::string name = "(";
name += std::to_string(history.size());
name += ") ";
name += interval.first;
unorderedIntervalVector.emplace_back(total_difference, name);
} else {
const auto difference = interval.second.end - interval.second.start;
if (ignoreNegatives && difference < 0)
continue;
unorderedIntervalVector.emplace_back(difference, interval.first);
}
}
std::sort(unorderedIntervalVector.begin(), unorderedIntervalVector.end(), timeCompare);
string::TableFormatter formatter{profileName};
formatter.addColumn({"Order"});
formatter.addColumn({"Interval"});
formatter.addColumn({"Time (ns)"});
formatter.addColumn({"Time (ms)"});
int index = 1;
for (const auto& interval : unorderedIntervalVector) {
formatter.addRow(
{std::to_string(index++), interval.name, std::to_string(interval.difference), std::to_string(interval.difference / 1000000.0)}
);
}
std::vector<std::string> updatedLines;
const auto& lines = formatter.createTable(true, true);
for (const auto& line : lines)
updatedLines.emplace_back(line + "\n");
print(updatedLines, loggingLevel);
}
void discardProfiles() {
profiles = {};
}
@ -175,7 +165,10 @@ namespace blt::profiling {
profiles[profileName].points = {};
}
std::vector<capture_interval> getAllIntervals(const std::string& profileName, const std::string& intervalName) {
std::vector<capture_interval> getAllIntervals(
const std::string& profileName, const std::string& intervalName
) {
return profiles[profileName].historicalIntervals[intervalName];
}
}

View File

@ -112,11 +112,11 @@ inline void nbt_read_tests(){
BLT_INFO("NBT Block %s Stream Correctly? %s;\n", size_str.c_str(), nbt_block_large_correct ? "True" : "False");
}
BLT_PRINT_ORDERED("nbt read");
BLT_PRINT_PROFILE("nbt read");
BLT_TRACE("{BLANK_LINE}");
BLT_PRINT_ORDERED("nbt read block");
BLT_PRINT_PROFILE("nbt read block");
BLT_TRACE("{BLANK_LINE}");
BLT_PRINT_ORDERED("nbt read individual");
BLT_PRINT_PROFILE("nbt read individual");
delete[] read_buffer;
delete[] read_block_buffer;
@ -177,9 +177,9 @@ inline void nbt_write_tests(){
BLT_INFO("NBT %s Individual Write Correctly? %s;\n", size_str.c_str(), results ? "True" : "False");
}
BLT_PRINT_ORDERED("nbt write individual");
BLT_PRINT_PROFILE("nbt write individual");
BLT_TRACE("");
BLT_PRINT_ORDERED("nbt write block");
BLT_PRINT_PROFILE("nbt write block");
}
inline void nbt_tests(){

View File

@ -40,8 +40,6 @@ static void runProfilingAndTableTests() {
BLT_PRINT_PROFILE("Help", blt::logging::LOG_LEVEL::TRACE);
BLT_TRACE("");
BLT_PRINT_ORDERED("Help", blt::logging::LOG_LEVEL::TRACE);
BLT_TRACE("");
blt::string::TableFormatter formatter;
formatter.addColumn(blt::string::TableColumn{"Test1"});

View File

@ -8,10 +8,109 @@
#define BLT_TESTS_QUEUE_TESTS_H
#include <queue>
#include <blt/std/queue.h>
#include <blt/std/logging.h>
#include <blt/profiling/profiler.h>
#include <array>
#include <blt/std/random.h>
static inline void test_queues(){
std::array<int, 100000> values{};
std::queue<int> base_queue{};
blt::flat_queue<int> blt_flat_queue{};
blt::flat_stack<int> blt_flat_stack{};
blt::node_queue<int> blt_node_queue{};
static inline void fill_queues(){
BLT_START_INTERVAL("Insert", "std::queue");
for (const auto& value : values)
base_queue.push(value);
BLT_END_INTERVAL("Insert", "std::queue");
BLT_START_INTERVAL("Insert", "blt::flat_queue");
for (const auto& value : values)
blt_flat_queue.push(value);
BLT_END_INTERVAL("Insert", "blt::flat_queue");
BLT_START_INTERVAL("Insert", "blt::flat_stack");
for (const auto& value : values)
blt_flat_stack.push(value);
BLT_END_INTERVAL("Insert", "blt::flat_stack");
BLT_START_INTERVAL("Insert", "blt::node_queue");
for (const auto& value : values)
blt_node_queue.push(value);
BLT_END_INTERVAL("Insert", "blt::node_queue");
}
static inline void validate(){
bool std_valid = true;
bool flat_valid = true;
bool node_valid = true;
bool stack_valid = true;
BLT_START_INTERVAL("Access", "std::queue");
for (const auto& value : values) {
auto front = base_queue.front();
if (front != value)
std_valid = false;
base_queue.pop();
}
BLT_END_INTERVAL("Access", "std::queue");
BLT_START_INTERVAL("Access", "blt::flat_queue");
for (const auto& value : values) {
auto front = blt_flat_queue.front();
if (front != value)
flat_valid = false;
blt_flat_queue.pop();
}
BLT_END_INTERVAL("Access", "blt::flat_queue");
BLT_START_INTERVAL("Access", "blt::flat_stack");
for (const auto& value : values) {
auto front = blt_flat_stack.top();
if (front != value)
stack_valid = false;
blt_flat_stack.pop();
}
BLT_END_INTERVAL("Access", "blt::flat_stack");
BLT_START_INTERVAL("Access", "blt::node_queue");
for (const auto& value : values) {
auto front = blt_node_queue.front();
if (front != value)
node_valid = false;
blt_node_queue.pop();
}
BLT_END_INTERVAL("Access", "blt::node_queue");
if (!std_valid)
BLT_ERROR("std::queue invalid!");
if (!flat_valid)
BLT_ERROR("blt::flat_queue invalid!");
if (!node_valid)
BLT_ERROR("blt::node_queue invalid!");
if (!stack_valid)
BLT_ERROR("blt::stack invalid!");
}
static inline void test_queues() {
blt::random<int, std::uniform_int_distribution> rand{1, std::numeric_limits<int>::max()};
for (int& value : values){
value = rand.get();
}
fill_queues();
validate();
BLT_PRINT_PROFILE("Insert", blt::logging::LOG_LEVEL::INFO, true);
BLT_PRINT_PROFILE("Access", blt::logging::LOG_LEVEL::INFO, true);
}
#endif //BLT_TESTS_QUEUE_TESTS_H