Skip to content

Instantly share code, notes, and snippets.

@Boostibot
Last active May 7, 2025 15:20
Show Gist options
  • Save Boostibot/483d36a34bc8092f207a456e5d4217dc to your computer and use it in GitHub Desktop.
Save Boostibot/483d36a34bc8092f207a456e5d4217dc to your computer and use it in GitHub Desktop.
Get RDTSC frequency from cpuid
#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, &regs.eax, &regs.ebx, &regs.ecx, &regs.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;
}
@pps83
Copy link

pps83 commented May 7, 2025

FYI, code in tsc_frequency_from_cpu_id is clearly buggy. There are two calls h15 = cpuid(0x15);, while it looks like second one was meant to be h16 = cpuid(0x16);? (h16 is also zero initialized and tested in the function, while never set).

Also, shouldn't it be 0x15 instead of 15: https://gist.github.com/Boostibot/483d36a34bc8092f207a456e5d4217dc#file-cpuid_tsc_freq-c-L54. Same about 16 a couple of lines bellow

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