cOMS/log/PerformanceProfiler.h

150 lines
4.2 KiB
C

/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_LOG_PERFORMANCE_PROFILER_H
#define TOS_LOG_PERFORMANCE_PROFILER_H
#include "../stdlib/Types.h"
#include "../platform/win32/TimeUtils.h"
#include "../architecture/Intrinsics.h"
#include "../compiler/CompilerUtils.h"
struct PerformanceProfileResult {
const char* name;
atomic_64 int64 total_time;
atomic_64 int64 total_cycle;
atomic_64 int64 self_time;
atomic_64 int64 self_cycle;
// Required for manual start/stop otherwise we would have to use one of the existing values above,
// which corrupts them for rendering
atomic_64 int64 tmp_time;
atomic_64 int64 tmp_cycle;
PerformanceProfileResult* parent;
};
static PerformanceProfileResult* _perf_stats = NULL;
struct PerformanceProfiler;
static PerformanceProfiler** _perf_current_scope = NULL; // Used when sharing profiler across dlls and threads (threads unlikely)
static PerformanceProfiler* _perf_current_scope_internal; // Used when in dll or thread and no shared pointer found
struct PerformanceProfiler {
const char* name;
int32 id;
int64 start_time;
int64 total_time;
int64 self_time;
int64 start_cycle;
int64 total_cycle;
int64 self_cycle;
PerformanceProfiler* parent;
PerformanceProfiler(int32 id, const char* scope_name) : id(id) {
name = scope_name;
start_time = time_mu();
start_cycle = intrin_timestamp_counter();
total_time = 0;
total_cycle = 0;
self_time = 0;
self_cycle = 0;
if (_perf_current_scope) {
parent = *_perf_current_scope;
*_perf_current_scope = this;
} else {
parent = _perf_current_scope_internal;
_perf_current_scope_internal = this;
}
}
~PerformanceProfiler() {
uint64 end_time = time_mu();
uint64 end_cycle = intrin_timestamp_counter();
total_time = OMS_MAX(end_time - start_time, 0);
total_cycle = OMS_MAX(end_cycle - start_cycle, 0);
self_time += total_time;
self_cycle += total_cycle;
if (parent) {
parent->self_time -= total_time;
parent->self_cycle -= total_cycle;
}
// Store result
PerformanceProfileResult* perf = &_perf_stats[id];
perf->name = name;
perf->total_time = total_time;
perf->total_cycle = total_cycle;
perf->self_time = self_time;
perf->self_cycle = self_cycle;
// @todo create reference to parent result
if (_perf_current_scope) {
*_perf_current_scope = parent;
} else {
_perf_current_scope_internal = parent;
}
}
};
void performance_profiler_reset(uint32 id)
{
PerformanceProfileResult* perf = &_perf_stats[id];
perf->total_time = 0;
perf->total_cycle = 0;
perf->self_time = 0;
perf->self_cycle = 0;
}
void performance_profiler_start(uint32 id, const char* name)
{
PerformanceProfileResult* perf = &_perf_stats[id];
perf->name = name;
perf->tmp_time = time_mu();
perf->tmp_cycle = intrin_timestamp_counter();
}
void performance_profiler_end(uint32 id)
{
PerformanceProfileResult* perf = &_perf_stats[id];
perf->total_time = time_mu() - perf->tmp_time;
perf->total_cycle = intrin_timestamp_counter() - perf->tmp_cycle;
perf->self_time = perf->total_time;
perf->self_cycle = perf->self_cycle;
}
#if (!DEBUG && !INTERNAL) || RELEASE
#define PROFILE_SCOPE(id, name) ((void) 0)
#define PROFILE(id) ((void) 0)
#define PROFILE_START(id, name) ((void) 0)
#define PROFILE_END(id) ((void) 0)
#define PROFILE_RESET(id) ((void) 0)
#else
#define PROFILE_SCOPE(id, name) PerformanceProfiler __profile_scope_##id(id, name)
// Only this function can properly handle self-time calculation
// Use this whenever you want to profile an entire function
#define PROFILE(id) PROFILE_SCOPE(id, __func__)
#define PROFILE_START(id, name) performance_profiler_start(id, name)
#define PROFILE_END(id) performance_profiler_end(id)
#define PROFILE_RESET(id) performance_profiler_reset((id))
#endif
#endif