Skip to content

Instantly share code, notes, and snippets.

@nickolayl
Created May 25, 2020 11:26
Show Gist options
  • Save nickolayl/f6651e978db145141faa1d986182014f to your computer and use it in GitHub Desktop.
Save nickolayl/f6651e978db145141faa1d986182014f to your computer and use it in GitHub Desktop.
Cross-platform, drop-in, high resolution timer for C/C++ projects.
/* ----------------------------------------------------------------------- */
/*
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
}
/* ----------------------------------------------------------------------- */
@SollyBunny
Copy link

SollyBunny commented Jun 29, 2024

This is great, but has a few problems

  1. Doesn't support posix like non linux systems, I've just made it default to posix / linux
  2. Weird indentation
  3. Doens't work in stricter C contexts (posix source undefined, mixed code and declerations, empty function args)

Here's my version:

/*
	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.
	https://gist.github.com/nickolayl/f6651e978db145141faa1d986182014f
*/
#include <stdint.h>
#if defined(__APPLE__)
	#include <mach/mach_time.h>
#elif defined(_WIN32)
	#include <windows.h>
#else
	#ifdef _POSIX_C_SOURCE
		#define NS_OLD_POSIX_C_SOURCE _POSIX_C_SOURCE
		#undef _POSIX_C_SOURCE
	#endif
	#define _POSIX_C_SOURCE 199309L
	#ifndef __USE_POSIX199309
		#define __USE_POSIX199309
		#define NS_DEFINED__USE_POSIX199309
	#endif
	#include <time.h>
	#ifdef NS_DEFINED__USE_POSIX199309
		#undef __USE_POSIX199309
	#endif
	#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
#endif
static inline uint64_t ns(void) {
	static uint8_t ns_inited = 0;
	#if defined(__APPLE__)
		static mach_timebase_info_data_t info;
		uint64_t now;
		if (ns_inited == 0) {
			mach_timebase_info(&info);
			ns_inited = 1;
		}
		now = mach_absolute_time();
		now *= info.numer;
		now /= info.denom;
		return now;
	#elif defined(_WIN32)
		static LARGE_INTEGER win_frequency;
		LARGE_INTEGER now;
		if (ns_inited == 0) {
			QueryPerformanceFrequency(&win_frequency);
			ns_inited = 1;
		}
		QueryPerformanceCounter(&now);
		return (uint64_t)((1.0e9 * now.QuadPart) / win_frequency.QuadPart);
	#else
		static struct timespec linux_rate;
		uint64_t now;
		struct timespec spec;
		if (ns_inited == 0) {
			clock_getres(NS_CLOCK_ID, &linux_rate);
			ns_inited = 1;
		}
		clock_gettime(NS_CLOCK_ID, &spec);
		now = spec.tv_sec * (uint64_t)1.0e9 + spec.tv_nsec;
		return now;
	#endif
}

@heysokam
Copy link

heysokam commented May 28, 2025

#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 stores last and current time references, so that duration = last-current can be computed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment