mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-11 11:18:40 +00:00
254 lines
9.2 KiB
C
254 lines
9.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 "../thread/Spinlock.cpp"
|
|
#include "../thread/Atomic.h"
|
|
#include "../system/Allocator.h"
|
|
#include "../hash/GeneralHash.h"
|
|
#include "../architecture/Intrinsics.h"
|
|
#include "../compiler/CompilerUtils.h"
|
|
#include "Log.h"
|
|
|
|
#ifndef PERFORMANCE_PROFILE_STATS
|
|
#define PERFORMANCE_PROFILE_STATS 1
|
|
enum TimingStats {
|
|
PROFILE_TEMP, // used for quick test debugging, not for permanent use
|
|
|
|
PROFILE_FILE_UTILS,
|
|
PROFILE_BUFFER_ALLOC,
|
|
PROFILE_CHUNK_ALLOC,
|
|
PROFILE_RING_ALLOC,
|
|
PROFILE_CMD_ITERATE,
|
|
PROFILE_CMD_FONT_LOAD_SYNC,
|
|
PROFILE_CMD_SHADER_LOAD_SYNC,
|
|
PROFILE_CMD_LAYOUT_LOAD_SYNC,
|
|
PROFILE_CMD_THEME_LOAD_SYNC,
|
|
PROFILE_CMD_UI_LOAD_SYNC,
|
|
PROFILE_LAYOUT_FROM_DATA,
|
|
PROFILE_LAYOUT_FROM_THEME,
|
|
PROFILE_THEME_FROM_THEME,
|
|
PROFILE_AUDIO_BUFFER_FILLABLE,
|
|
PROFILE_AUDIO_PLAY_BUFFER,
|
|
PROFILE_AUDIO_MIXER_MIX,
|
|
PROFILE_ASSET_ARCHIVE_LOAD,
|
|
PROFILE_ASSET_ARCHIVE_ASSET_LOAD,
|
|
PROFILE_VERTEX_RECT_CREATE,
|
|
PROFILE_VERTEX_TEXT_CREATE,
|
|
|
|
PROFILE_SIZE,
|
|
};
|
|
#endif
|
|
|
|
struct PerformanceProfileResult {
|
|
atomic_64 const char* name;
|
|
|
|
atomic_64 int64 total_cycle;
|
|
atomic_64 int64 self_cycle;
|
|
|
|
atomic_32 uint32 counter;
|
|
uint32 parent;
|
|
};
|
|
static PerformanceProfileResult* _perf_stats = NULL;
|
|
static int32* _perf_active = NULL;
|
|
|
|
struct PerformanceProfiler;
|
|
static thread_local PerformanceProfiler** _perf_current_scope = NULL; // Used when sharing profiler across dlls and threads (threads unlikely)
|
|
static thread_local PerformanceProfiler* _perf_current_scope_internal; // Used when in dll or thread and no shared pointer found
|
|
struct PerformanceProfiler {
|
|
bool is_active;
|
|
|
|
const char* name;
|
|
const char* info_msg;
|
|
int32 id;
|
|
|
|
int64 start_cycle;
|
|
int64 total_cycle;
|
|
int64 self_cycle;
|
|
|
|
PerformanceProfiler* parent;
|
|
|
|
bool auto_log;
|
|
bool is_stateless;
|
|
|
|
// @question Do we want to make the self cost represent calls * "self_time/cycle"
|
|
// Stateless allows to ONLY output to log instead of storing the performance data in an array
|
|
PerformanceProfiler(
|
|
int32 id, const char* scope_name, const char* info = NULL,
|
|
bool stateless = false, bool should_log = false
|
|
) {
|
|
if (!_perf_active || !*_perf_active) {
|
|
this->is_active = false;
|
|
|
|
return;
|
|
}
|
|
|
|
this->id = id;
|
|
++_perf_stats[id].counter;
|
|
|
|
this->name = scope_name;
|
|
this->info_msg = info;
|
|
this->is_stateless = stateless;
|
|
this->auto_log = stateless || should_log;
|
|
|
|
this->start_cycle = intrin_timestamp_counter();
|
|
this->total_cycle = 0;
|
|
this->self_cycle = 0;
|
|
|
|
if (this->is_stateless) {
|
|
this->parent = NULL;
|
|
} else {
|
|
if (_perf_current_scope) {
|
|
this->parent = *_perf_current_scope;
|
|
*_perf_current_scope = this;
|
|
} else {
|
|
this->parent = _perf_current_scope_internal;
|
|
_perf_current_scope_internal = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
~PerformanceProfiler() {
|
|
if (!this->is_active) {
|
|
return;
|
|
}
|
|
|
|
uint64 end_cycle = intrin_timestamp_counter();
|
|
this->total_cycle = OMS_MAX(end_cycle - start_cycle, 0);
|
|
this->self_cycle += total_cycle;
|
|
|
|
// Store result
|
|
PerformanceProfileResult temp_perf = {};
|
|
PerformanceProfileResult* perf = this->is_stateless ? &temp_perf : &_perf_stats[this->id];
|
|
|
|
perf->name = this->name;
|
|
perf->total_cycle = this->total_cycle;
|
|
perf->self_cycle = this->self_cycle;
|
|
|
|
if (!this->is_stateless) {
|
|
if (this->parent) {
|
|
this->parent->self_cycle -= this->total_cycle;
|
|
perf->parent = this->parent->id;
|
|
}
|
|
|
|
if (_perf_current_scope) {
|
|
*_perf_current_scope = this->parent;
|
|
} else {
|
|
_perf_current_scope_internal = this->parent;
|
|
}
|
|
}
|
|
|
|
if (this->auto_log) {
|
|
if (this->info_msg && this->info_msg[0]) {
|
|
LOG_FORMAT_2(
|
|
"%s (%s): %l cycles",
|
|
{
|
|
{LOG_DATA_CHAR_STR, (void *) perf->name},
|
|
{LOG_DATA_CHAR_STR, (void *) this->info_msg},
|
|
{LOG_DATA_INT64, (void *) &perf->total_cycle},
|
|
}
|
|
);
|
|
} else {
|
|
LOG_FORMAT_2(
|
|
"%s: %l cycles",
|
|
{
|
|
{LOG_DATA_CHAR_STR, (void *) perf->name},
|
|
{LOG_DATA_INT64, (void *) &perf->total_cycle},
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
inline
|
|
void performance_profiler_reset(int32 id) noexcept
|
|
{
|
|
PerformanceProfileResult* perf = &_perf_stats[id];
|
|
perf->total_cycle = 0;
|
|
perf->self_cycle = 0;
|
|
perf->parent = NULL;
|
|
}
|
|
|
|
inline
|
|
void performance_profiler_start(int32 id, const char* name) noexcept
|
|
{
|
|
PerformanceProfileResult* perf = &_perf_stats[id];
|
|
perf->name = name;
|
|
perf->self_cycle = -((int64) intrin_timestamp_counter());
|
|
}
|
|
|
|
inline
|
|
void performance_profiler_end(int32 id) noexcept
|
|
{
|
|
PerformanceProfileResult* perf = &_perf_stats[id];
|
|
perf->total_cycle = intrin_timestamp_counter() + perf->self_cycle;
|
|
perf->self_cycle = perf->total_cycle;
|
|
}
|
|
|
|
// @question Do we want different levels of PROFILE_VERBOSE and PROFILE_STATELESS same as in Log.h
|
|
// This would allow us to go ham in a lot of functions (e.g. file reading)
|
|
|
|
#if LOG_LEVEL == 4
|
|
// Only these function can properly handle self-time calculation
|
|
// Use these whenever you want to profile an entire function
|
|
#define PROFILE(id) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__)
|
|
#define PROFILE_VERBOSE(id, info) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__, (info), false, true)
|
|
#define PROFILE_STATELESS(id, info) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__, (info), true, true)
|
|
|
|
#define PROFILE_START(id, name) if(_perf_active && *_perf_active) performance_profiler_start((id), (name))
|
|
#define PROFILE_END(id) if(_perf_active && *_perf_active) performance_profiler_end((id))
|
|
#define PROFILE_SCOPE(id, name) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), (name))
|
|
#define PROFILE_RESET(id) if(_perf_active && *_perf_active) performance_profiler_reset((id))
|
|
#elif LOG_LEVEL == 3
|
|
// Only these function can properly handle self-time calculation
|
|
// Use these whenever you want to profile an entire function
|
|
#define PROFILE(id) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__)
|
|
#define PROFILE_VERBOSE(id, info) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__, (info), false, true)
|
|
#define PROFILE_STATELESS(id, info) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__, (info), true, true)
|
|
|
|
#define PROFILE_START(id, name) if(_perf_active && *_perf_active) performance_profiler_start((id), (name))
|
|
#define PROFILE_END(id) if(_perf_active && *_perf_active) performance_profiler_end((id))
|
|
#define PROFILE_SCOPE(id, name) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), (name))
|
|
#define PROFILE_RESET(id) if(_perf_active && *_perf_active) performance_profiler_reset((id))
|
|
#elif LOG_LEVEL == 2
|
|
// Only these function can properly handle self-time calculation
|
|
// Use these whenever you want to profile an entire function
|
|
#define PROFILE(id) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__)
|
|
#define PROFILE_VERBOSE(id, info) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__, (info), false, true)
|
|
#define PROFILE_STATELESS(id, info) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), __func__, (info), true, true)
|
|
|
|
#define PROFILE_START(id, name) if(_perf_active && *_perf_active) performance_profiler_start((id), (name))
|
|
#define PROFILE_END(id) if(_perf_active && *_perf_active) performance_profiler_end((id))
|
|
#define PROFILE_SCOPE(id, name) PerformanceProfiler __profile_scope_##__func__##_##__LINE__((id), (name))
|
|
#define PROFILE_RESET(id) if(_perf_active && *_perf_active) performance_profiler_reset((id))
|
|
#elif LOG_LEVEL == 1
|
|
#define PROFILE(id) ((void) 0)
|
|
#define PROFILE_VERBOSE(name) ((void) 0)
|
|
#define PROFILE_STATELESS(id, info) ((void) 0)
|
|
|
|
#define PROFILE_START(id, name) ((void) 0)
|
|
#define PROFILE_END(id) ((void) 0)
|
|
#define PROFILE_SCOPE(id, name) ((void) 0)
|
|
#define PROFILE_RESET(id) ((void) 0)
|
|
#elif LOG_LEVEL == 0
|
|
#define PROFILE(id) ((void) 0)
|
|
#define PROFILE_VERBOSE(name) ((void) 0)
|
|
#define PROFILE_STATELESS() ((void) 0)
|
|
|
|
#define PROFILE_START(id, name) ((void) 0)
|
|
#define PROFILE_END(id) ((void) 0)
|
|
#define PROFILE_SCOPE(id, name) ((void) 0)
|
|
#define PROFILE_RESET(id) ((void) 0)
|
|
#endif
|
|
|
|
#endif |