Musa-STL-Cpp/lib/Base/Timing.h

204 lines
6.2 KiB
C

// #Timing API:
#define System_Timed_Block_Print(name) \
system_timed_block_print Concat(_sys_timed_block_print_guard, __LINE__)(name)
#define Timed_Block_Print(name) \
timed_block_print Concat(_timed_block_print_guard, __LINE__)(name)
// This is for timing stuff that happens where context is not available.
// memory will leak from this operation. #MemoryLeak #NoContext
#define Timed_Block_Print_No_Context(name) \
timed_block_print_no_context Concat(_timed_block_print_guard, __LINE__)(name)
force_inline u64 rdtsc();
#if OS_WINDOWS
#include <intrin.h>
#pragma intrinsic(__rdtsc)
force_inline u64 rdtsc() {
return __rdtsc();
}
#endif
global u32 g_cpu_base_frequency_megahertz;
void set_cpu_base_frequency (u32 frequency_megahertz) {
g_cpu_base_frequency_megahertz = frequency_megahertz;
if (!g_cpu_base_frequency_megahertz) {
printf("[Warning] Unable to set CPU base frequency by reading CPUID. Falling back to default value of 3000 MHz\n");
g_cpu_base_frequency_megahertz = 3000; // default value
} else {
printf("CPU Base Frequency detected as %d MHz\n", g_cpu_base_frequency_megahertz);
}
}
string format_time_seconds_no_context (f64 time) { // #MemoryLeak #NoContext
if (time < 1.0e-6) { return format_string("%1.2f ns", time * 1.0e9); }
if (time < 1.0e-3) { return format_string("%1.3f us", time * 1.0e6); }
if (time < 1) { return format_string("%1.3f ms", time * 1.0e3); }
return format_string("%1.3f s", time);
}
string format_cycles_no_context (u64 ticks) { // #MemoryLeak #NoContext
string units[5] = { "cycles", "K cycles", "M cycles", "B cycles", "T cycles" };
f64 count_f64 = (f64)ticks;
s64 unit_index = 0;
while (count_f64 >= 1000 && unit_index < 4) { // 4 is from (units.count-1)
count_f64 /= 1000.0;
unit_index += 1;
}
return format_string("%1.2f %s", count_f64, units[unit_index].data);
}
string format_time_seconds (f64 time) {
if (time < 1.0e-6) { return format_string("%1.2f ns", time * 1.0e9); }
if (time < 1.0e-3) { return format_string("%1.3f us", time * 1.0e6); }
if (time < 1) { return format_string("%1.3f ms", time * 1.0e3); }
return format_string("%1.3f s", time);
}
string format_int_with_commas (s64 value) {
char tmp[32];
snprintf(tmp, sizeof(tmp), "%lld", value);
s32 tmp_len = (s32)strlen(tmp);
s32 commas = (tmp_len - 1) / 3;
s32 new_len = tmp_len + commas;
string new_string;
new_string.data = NewArray<u8>(new_len + 1);
new_string.count = new_len;
s32 i = tmp_len - 1;
s32 j = new_len - 1;
s32 group = 0;
while (i >= 0) {
new_string[j] = tmp[i];
j -= 1;
i -= 1;
group += 1;
if (group == 3 && i >= 0) {
new_string[j] = ',';
j -= 1;
group = 0;
}
}
return new_string;
}
string format_int_with_leading_spaces (s32 value, s32 digits_to_show) {
Assert(digits_to_show < 12); // no way you have a trillion files, bro.
string new_string;
new_string.data = NewArray<u8>(32 + 1);
new_string.count = snprintf((char*)new_string.data, (size_t)33, (char*)"%*d", digits_to_show, value);
return new_string;
}
string format_cycles (u64 ticks) {
string units[5] = { "cycles", "K cycles", "M cycles", "B cycles", "T cycles" };
f64 count_f64 = (f64)ticks;
s64 unit_index = 0;
while (count_f64 >= 1000 && unit_index < 4) { // 4 is from (units.count-1)
count_f64 /= 1000.0;
unit_index += 1;
}
return format_string("%1.2f %s", count_f64, units[unit_index].data);
}
string format_bytes (s64 bytes, s32 trailing_width = 3) {
if (bytes == 0) return copy_string(" - B");
string units[6] = { "B", "KB", "MB", "GB", "TB", "PB" };
f64 count_f64 = (f64)bytes;
s32 unit_index = 0;
while (count_f64 >= 1024 && unit_index < (5)) {
count_f64 /= 1024.0;
unit_index += 1;
}
// This makes the trailing width param kinda pointless... idk.
if (unit_index == 0) trailing_width = 0;
// if (unit_index == 1) trailing_width = 2;
switch (trailing_width) {
case 0: return format_string("%.0f %s", count_f64, units[unit_index].data);
case 1: return format_string("%.1f %s", count_f64, units[unit_index].data);
case 2: return format_string("%.2f %s", count_f64, units[unit_index].data);
case 3: return format_string("%.3f %s", count_f64, units[unit_index].data);
default: break;
}
Assert(trailing_width >= 0 && trailing_width <= 3);
return "";
}
struct timed_block_print {
string block_name;
u64 start_tick;
timed_block_print(string _block_name) {
Assert(g_cpu_base_frequency_megahertz != 0);
Assert(thread_local_context != nullptr); // we need temp allocator initialized!
block_name = _block_name;
start_tick = rdtsc();
}
~timed_block_print() {
u64 end_tick = rdtsc();
u64 tick_difference = end_tick - start_tick;
f64 ticks_f64 = (f64)tick_difference;
f64 elapsed_time_seconds = ticks_f64 / (f64)((s64)g_cpu_base_frequency_megahertz * 1000000);
push_allocator(temp());
log("[Timed_Block %s]: %s (%s)", block_name.data, format_time_seconds(elapsed_time_seconds).data, format_cycles(tick_difference).data);
}
};
struct timed_block_print_no_context {
string block_name;
u64 start_tick;
timed_block_print_no_context(string _block_name) {
Assert(g_cpu_base_frequency_megahertz != 0);
block_name = _block_name;
start_tick = rdtsc();
}
~timed_block_print_no_context() {
u64 end_tick = rdtsc();
u64 tick_difference = end_tick - start_tick;
f64 ticks_f64 = (f64)tick_difference;
f64 elapsed_time_seconds = ticks_f64 / (f64)((s64)g_cpu_base_frequency_megahertz * 1000000);
printf("[Timed_Block(No Context) %s]: %s (%s)\n",
block_name.data,
format_time_seconds_no_context(elapsed_time_seconds).data,
format_cycles_no_context(tick_difference).data);
}
};
struct system_timed_block_print {
string block_name;
f64 start_time;
system_timed_block_print(string _block_name) {
block_name = _block_name;
start_time = GetUnixTimestamp();
}
~system_timed_block_print() {
f64 end_time = GetUnixTimestamp();
f64 elapsed_time_seconds = end_time - start_time;
push_allocator(temp());
log("[Timed_Block %s]: %s", block_name.data, format_time_seconds(elapsed_time_seconds).data);
}
};