Created
June 23, 2025 18:14
-
-
Save Zelmoghazy/023b427692587afb68b05691fb95d71c to your computer and use it in GitHub Desktop.
Very simple profiler in C
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 <stdio.h> | |
#include <time.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <assert.h> | |
#ifdef _WIN32 | |
#include <windows.h> | |
#else | |
#include <sys/time.h> | |
#include <unistd.h> | |
#endif | |
#define SIZE 10000000 | |
#define DEFER(begin, end) \ | |
for(int _defer_ = ((begin), 0); !_defer_; _defer_ = 1, (end)) | |
#define CONCAT(a, b) a##b | |
#define CONCAT_AUX(a,b) CONCAT(a,b) | |
/* | |
Get the number of OS ticks-per-second | |
*/ | |
static uint64_t get_timer_freq(void) | |
{ | |
#ifdef _WIN32 | |
LARGE_INTEGER Freq; | |
QueryPerformanceFrequency(&Freq); | |
return Freq.QuadPart; | |
#else | |
return 1000000000ULL; // nanosecond resolution (clock_gettime) | |
#endif | |
} | |
/* | |
Get the time elapsed since the last reset in nanoseconds | |
*/ | |
static uint64_t get_time(void) | |
{ | |
#ifdef _WIN32 | |
LARGE_INTEGER Value; | |
QueryPerformanceCounter(&Value); | |
// We know the time of a single tick in seconds we just convert it to nanoseconds | |
// and multiply by the number of elapsed ticks | |
uint64_t elapsed_ns = Value.QuadPart * 1000000000ULL; | |
elapsed_ns /= get_timer_freq(); | |
return elapsed_ns; | |
#else | |
struct timespec ts; | |
clock_gettime(CLOCK_MONOTONIC, &ts); | |
uint64_t result = get_timer_freq() * (uint64_t)ts.tv_sec + (uint64_t)ts.tv_nsec; | |
return result; | |
#endif | |
} | |
typedef struct | |
{ | |
uint64_t start_time; | |
const char* label; | |
} prof_zone; | |
prof_zone prof_start(const char* name) | |
{ | |
prof_zone p; | |
p.start_time = get_time(); | |
p.label = name; | |
printf("[PROFILE] Starting: %s\n", name); | |
return p; | |
} | |
void prof_end(prof_zone* p) | |
{ | |
uint64_t end_time = get_time(); | |
double elapsed_ns = (double)(end_time - p->start_time); | |
double elapsed_ms = elapsed_ns / 1000000.0; | |
printf("[PROFILE] Finished: %s (%.6f ms)\n", p->label, elapsed_ms); | |
} | |
#define PROFILE(name) \ | |
prof_zone CONCAT_AUX(_prof_, __LINE__) = prof_start(name); \ | |
DEFER(0, prof_end(&CONCAT_AUX(_prof_, __LINE__))) | |
void expensive_operation(void) | |
{ | |
volatile long sum = 0; | |
for(int i = 0; i < SIZE; i++) { | |
sum += i; | |
} | |
} | |
void sleep_test(uint32_t ms) | |
{ | |
#ifdef _WIN32 | |
Sleep(ms); | |
#else | |
usleep(ms * 1000); | |
#endif | |
} | |
void string_processing(void) | |
{ | |
char *buffer = NULL; | |
PROFILE("Allocating memory") | |
{ | |
buffer = malloc(SIZE); | |
if(!buffer) return; | |
} | |
PROFILE("Actual Work") | |
{ | |
for(int i = 0; i < SIZE; i++) { | |
sprintf(buffer, "Processing item %d", i); | |
size_t len = strlen(buffer); | |
(void)len; | |
} | |
} | |
PROFILE("Freeing memory"){ | |
free(buffer); | |
} | |
} | |
int main(void) | |
{ | |
PROFILE("main") | |
{ | |
PROFILE("expensive_operation") | |
{ | |
expensive_operation(); | |
} | |
PROFILE("string_processing") | |
{ | |
string_processing(); | |
} | |
PROFILE("sleep test") | |
{ | |
sleep_test(1000); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment