Last active
May 7, 2025 15:20
-
-
Save Boostibot/483d36a34bc8092f207a456e5d4217dc to your computer and use it in GitHub Desktop.
Get RDTSC frequency from cpuid
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 <stdbool.h> | |
#include <stdint.h> | |
typedef union Cpuid_Registers | |
{ | |
struct { | |
uint32_t eax; | |
uint32_t ebx; | |
uint32_t ecx; | |
uint32_t edx; | |
}; | |
uint32_t m[4]; | |
int mi[4]; | |
} Cpuid_Registers; | |
#if defined(_MSC_VER) && !defined(__clang__) | |
#include <intrin.h> | |
static Cpuid_Registers cpuid(uint32_t function_id) | |
{ | |
Cpuid_Registers regs = {0}; | |
__cpuid(regs.mi, function_id); | |
return regs; | |
} | |
#else | |
#include <cpuid.h> | |
static Cpuid_Registers cpuid(uint32_t function_id) | |
{ | |
Cpuid_Registers regs = {0}; | |
__get_cpuid(function_id, ®s.eax, ®s.ebx, ®s.ecx, ®s.edx); | |
return regs; | |
} | |
#endif | |
static bool supports_constant_tsc() | |
{ | |
Cpuid_Registers regs = {0}; | |
unsigned int max_extended = cpuid(0x80000000).m[0]; | |
if(max_extended >= 0x80000007) | |
regs = cpuid(0x80000007); | |
return !!(regs.edx & (1u << 8)); | |
} | |
static uint64_t tsc_frequency_from_cpu_id() | |
{ | |
//see https://en.wikipedia.org/wiki/CPUID#EAX=15h_and_EAX=16h:_CPU,_TSC,_Bus_and_Core_Crystal_Clock_Frequencies | |
unsigned int max_basic = cpuid(0x0).m[0]; | |
Cpuid_Registers h15 = {0}; | |
Cpuid_Registers h16 = {0}; | |
uint64_t freq = 0; | |
if(freq == 0 && max_basic >= 15) { | |
h15 = cpuid(0x15); | |
if(h15.ebx != 0 && h15.ecx != 0) | |
freq = h15.ecx*(h15.ebx/h15.eax); | |
} | |
if(freq == 0 && max_basic >= 16) { | |
h15 = cpuid(0x15); | |
if(h15.ebx == 0 && h16.eax != 0) | |
freq = (h16.eax * 10000000)*(h15.eax/h16.ebx); | |
} | |
return freq; | |
} | |
//RDTSC x86 | |
#ifdef _MSC_VER | |
#include <intrin.h> | |
static inline uint64_t rdtsc(){ | |
_ReadWriteBarrier(); | |
return __rdtsc(); | |
} | |
#else | |
#include <x86intrin.h> | |
static inline uint64_t rdtsc(){ | |
__asm__ __volatile__("":::"memory"); | |
return __rdtsc(); | |
} | |
#endif | |
//OS time | |
#if defined(_WIN32) || defined(_WIN64) | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
typedef int BOOL; | |
typedef union _LARGE_INTEGER LARGE_INTEGER; | |
__declspec(dllimport) BOOL __stdcall QueryPerformanceCounter(LARGE_INTEGER* out); | |
__declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* out); | |
#ifdef __cplusplus | |
} | |
#endif | |
static int64_t perf_counter() | |
{ | |
int64_t out = 0; | |
QueryPerformanceCounter((LARGE_INTEGER*) (void*) &out); | |
return out; | |
} | |
static int64_t perf_frequency() | |
{ | |
static int64_t freq = 0; | |
if(freq == 0) | |
QueryPerformanceFrequency((LARGE_INTEGER*) (void*) &freq); | |
return freq; | |
} | |
#elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) | |
#include <time.h> | |
static int64_t perf_counter() | |
{ | |
struct timespec ts = {0}; | |
(void) clock_gettime(CLOCK_MONOTONIC_RAW , &ts); | |
return (int64_t) ts.tv_nsec + ts.tv_sec * 1000000000LL; | |
} | |
static int64_t perf_frequency() | |
{ | |
return (int64_t) 1000000000LL; | |
} | |
#endif | |
#include <stdio.h> | |
#include <math.h> | |
int main() | |
{ | |
uint64_t tsc_frequency = tsc_frequency_from_cpu_id(); | |
if(tsc_frequency == 0) | |
{ | |
printf("Failed to obtain TSC frequency, exiting...\n"); | |
return 1; | |
} | |
printf("TSC frequency: %llu - %.4lfGHz (%s)\n", (unsigned long long) tsc_frequency, tsc_frequency*1e-9, supports_constant_tsc() ? "constant" : "varying"); | |
uint64_t start_osc = perf_counter(); | |
uint64_t start_tsc = rdtsc(); | |
double last_print = 0; | |
double print_every = 0.5; | |
double quit_after = 10; | |
for(;;) { | |
uint64_t now_osc = perf_counter(); | |
uint64_t now_tsc = rdtsc(); | |
double ellapsed_osc = (double) (now_osc - start_osc)/perf_frequency(); | |
double ellapsed_tsc = (double) (now_tsc - start_tsc)/tsc_frequency; | |
if(ellapsed_osc - last_print > print_every) | |
{ | |
printf("osc: %9.6lfs tsc: %9.6lfs diff: %9lluns\n", ellapsed_osc, ellapsed_tsc, | |
(unsigned long long) (fabs(ellapsed_osc - ellapsed_tsc)*1e9 + 0.5)); | |
last_print = ellapsed_osc; | |
} | |
if(ellapsed_osc > quit_after) | |
break; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FYI, code in
tsc_frequency_from_cpu_id
is clearly buggy. There are two callsh15 = cpuid(0x15);
, while it looks like second one was meant to beh16 = cpuid(0x16);
? (h16
is also zero initialized and tested in the function, while never set).Also, shouldn't it be
0x15
instead of15
: https://gist.github.com/Boostibot/483d36a34bc8092f207a456e5d4217dc#file-cpuid_tsc_freq-c-L54. Same about 16 a couple of lines bellow