Created
May 25, 2020 11:26
-
-
Save nickolayl/f6651e978db145141faa1d986182014f to your computer and use it in GitHub Desktop.
Cross-platform, drop-in, high resolution timer for C/C++ projects.
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
/* ----------------------------------------------------------------------- */ | |
/* | |
Easy embeddable cross-platform high resolution timer function. For each | |
platform we select the high resolution timer. You can call the 'ns()' | |
function in your file after embedding this. | |
*/ | |
#include <stdint.h> | |
#if defined(__linux) | |
# define HAVE_POSIX_TIMER | |
# include <time.h> | |
# ifdef CLOCK_MONOTONIC | |
# define CLOCKID CLOCK_MONOTONIC | |
# else | |
# define CLOCKID CLOCK_REALTIME | |
# endif | |
#elif defined(__APPLE__) | |
# define HAVE_MACH_TIMER | |
# include <mach/mach_time.h> | |
#elif defined(_WIN32) | |
# define WIN32_LEAN_AND_MEAN | |
# include <windows.h> | |
#endif | |
static uint64_t ns() { | |
static uint64_t is_init = 0; | |
#if defined(__APPLE__) | |
static mach_timebase_info_data_t info; | |
if (0 == is_init) { | |
mach_timebase_info(&info); | |
is_init = 1; | |
} | |
uint64_t now; | |
now = mach_absolute_time(); | |
now *= info.numer; | |
now /= info.denom; | |
return now; | |
#elif defined(__linux) | |
static struct timespec linux_rate; | |
if (0 == is_init) { | |
clock_getres(CLOCKID, &linux_rate); | |
is_init = 1; | |
} | |
uint64_t now; | |
struct timespec spec; | |
clock_gettime(CLOCKID, &spec); | |
now = spec.tv_sec * 1.0e9 + spec.tv_nsec; | |
return now; | |
#elif defined(_WIN32) | |
static LARGE_INTEGER win_frequency; | |
if (0 == is_init) { | |
QueryPerformanceFrequency(&win_frequency); | |
is_init = 1; | |
} | |
LARGE_INTEGER now; | |
QueryPerformanceCounter(&now); | |
return (uint64_t) ((1e9 * now.QuadPart) / win_frequency.QuadPart); | |
#endif | |
} | |
/* ----------------------------------------------------------------------- */ |
#include <stdint.h>
#if defined __APPLE__
#include <mach/mach_time.h>
typedef mach_timebase_info_data_t std_Time;
#elif defined _WIN32
#include <windows.h>
typedef LARGE_INTEGER std_Time;
#else // posix
#ifdef _POSIX_C_SOURCE
#define NS_OLD_POSIX_C_SOURCE _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#define _POSIX_C_SOURCE 199309L
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreserved-macro-identifier"
#ifndef __USE_POSIX199309
#define __USE_POSIX199309
#define NS_DEFINED__USE_POSIX199309
#endif
#include <time.h>
#ifdef NS_DEFINED__USE_POSIX199309
#undef __USE_POSIX199309
#endif
#pragma GCC diagnostic pop // "-Wreserved-macro-identifier"
#ifdef NS_OLD_POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE NS_OLD_POSIX_C_SOURCE
#endif
#ifdef CLOCK_MONOTONIC
#define NS_CLOCK_ID CLOCK_MONOTONIC
#else
#define NS_CLOCK_ID CLOCK_REALTIME
#endif
typedef struct timespec std_Time;
#endif
/// Monotonic Clock Data
typedef struct TimeData {
std_Time data;
uint64_t last;
uint64_t current;
} TimeData;
TimeData time_start ();
uint64_t time_nsec (TimeData* const clock);
#if defined _WIN32
static TimeData time_start_win () {
TimeData result = (TimeData){.data= (std_Time){}};
QueryPerformanceFrequency(&result.data);
return result;
}
#elif defined __APPLE__
static TimeData time_start_mac () {
TimeData result = (TimeData){.data= (std_Time){}};
mach_timebase_info(&result.data);
return result;
}
#else
static TimeData time_start_lnx () {
TimeData result = (TimeData){.data= (std_Time){}};
clock_getres(NS_CLOCK_ID, &result.data);
return result;
}
#endif
TimeData time_start () {
#if defined _WIN32
return time_start_win();
#elif defined __APPLE__
return time_start_mac();
#else
return time_start_lnx();
#endif
}
#if defined _WIN32
static uint64_t time_nsec_win (TimeData* const clock) {
std_Time time;
QueryPerformanceCounter(&time);
clock->last = clock->current;
clock->current = (uint64_t)((1.0e9 * time.QuadPart) / clock->data.QuadPart);
return clock->current;
}
#elif defined __APPLE__
static uint64_t time_nsec_mac (TimeData* const clock) {
clock->last = clock->current;
clock->current = mach_absolute_time();
clock->current *= clock->data.numer;
clock->current /= clock->data.denom;
return clock->current;
}
#else
static uint64_t time_nsec_lnx (TimeData* const clock) {
std_Time time;
clock_gettime(NS_CLOCK_ID, &time);
clock->last = clock->current;
clock->current = (uint64_t)time.tv_sec * (uint64_t)1.0e9 + (uint64_t)time.tv_nsec;
return clock->current;
}
#endif
uint64_t time_nsec (TimeData* const clock) {
#if defined _WIN32
return time_nsec_win(clock);
#elif defined __APPLE__
return time_nsec_mac(clock);
#else
return time_nsec_lnx(clock);
#endif
}
- Separation of concerns. Separate functions/sections for separate things.
- Types and functions are grouped by the section where they belong.
- Removed global variables (function scope static means global).
- The passed object for
nsec
storeslast
andcurrent
time references, so thatduration = last-current
can be computed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is great, but has a few problems
Here's my version: