Created
March 2, 2024 18:15
-
-
Save Boostibot/81214ab3fc923076d3d68e448af87979 to your computer and use it in GitHub Desktop.
A tiny c++ profiler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <chrono> | |
static int64_t perf_counter_ns() | |
{ | |
using namespace std::chrono; | |
return (int64_t) duration_cast<nanoseconds>(system_clock::now().time_since_epoch()).count(); | |
} | |
static double clock_s() | |
{ | |
static int64_t init_time = perf_counter_ns(); | |
int64_t now = perf_counter_ns(); | |
double clock = (double) (now - init_time) * 1e-9; | |
return clock; | |
} | |
struct Profile_Point { | |
int64_t counter_sum_ns; | |
int64_t runs; | |
bool is_in_progress; | |
Profile_Point* next; | |
const char* name; | |
const char* file; | |
const char* function; | |
int line; | |
}; | |
struct Profiles { | |
Profile_Point* profiles; | |
int64_t count; | |
}; | |
//Staic variables inside static inline functions in c++ | |
// are guaranteed to be truly global (ie shared across translation units) | |
static inline Profiles* profiles_get() | |
{ | |
static Profiles profiles = {0}; | |
return &profiles; | |
} | |
static void profiles_print() | |
{ | |
typedef long long lli; | |
printf("(%-3i) avg total runs location & name\n", (int) profiles_get()->count); | |
for(Profile_Point* point = profiles_get()->profiles; point != NULL; point = point->next) { | |
assert(point); | |
double ns_to_ms = 1e-3; | |
double avg_time = (double) point->counter_sum_ns / point->runs *ns_to_ms; | |
double total_time = point->counter_sum_ns*ns_to_ms; | |
printf("%13.4lf %13.2lf %10lli %s:%i ", avg_time, total_time, (lli) point->runs, point->function, point->line); | |
if(point->name && strlen(point->name) > 0) | |
printf("\"%s\"", point->name); | |
printf("\n"); | |
} | |
} | |
static void profiles_print_once_every(double seconds) | |
{ | |
double now = clock_s(); | |
static double last_time = 0; | |
if(last_time + seconds < now) | |
{ | |
printf("\n\n"); | |
last_time = now; | |
profiles_print(); | |
} | |
} | |
struct Profile_Running | |
{ | |
int64_t start = 0; | |
Profile_Point* point = NULL; | |
Profile_Running(Profile_Point* point, const char* name, const char* file, const char* function, int line) { | |
if(point->line == 0) | |
{ | |
point->name = name; | |
point->file = file; | |
point->function = function; | |
point->line = line; | |
point->runs = 0; | |
point->counter_sum_ns = 0; | |
point->next = profiles_get()->profiles; | |
profiles_get()->profiles = point; | |
profiles_get()->count += 1; | |
} | |
this->point = point; | |
start = perf_counter_ns(); | |
point->is_in_progress = true; | |
} | |
~Profile_Running() { | |
if(point->is_in_progress) | |
{ | |
point->is_in_progress = false; | |
int64_t end = perf_counter_ns(); | |
point->counter_sum_ns += end - start; | |
point->runs += 1; | |
} | |
} | |
}; | |
#define CONCAT_(a, b) a ## b | |
#define CONCAT(a, b) CONCAT_(a, b) | |
#define UNIQUE_NAME(name) CONCAT(name, __LINE__) | |
#ifndef _MSC_VER | |
#define __FUNCTION__ __func__ | |
#endif | |
//use this macro: PROFILE() or PROFILE("") | |
#define PROFILE(...) \ | |
static Profile_Point UNIQUE_NAME(__profile__) = {0}; \ | |
Profile_Running UNIQUE_NAME(__running__)(&UNIQUE_NAME(__profile__), "" __VA_ARGS__, __FILE__, __FUNCTION__, __LINE__) \ | |
void profile_example() | |
{ | |
PROFILE(); //profiles this scope (entire function) | |
{ | |
PROFILE("my_profile"); //profiles this scope and name it my_profile | |
//do some work... | |
} | |
profiles_print(); //print the profiles | |
// ... or print them with interval of at least 3 seconds | |
profiles_print_once_every(3); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment