mcap/modules/Basic/timing_x64.jai

84 lines
2.8 KiB
Plaintext

// RDTSC ticks, converted with the TSC frequency from CPUID leaf 0x15, or with the base frequency
// from leaf 0x16 where 0x15 is missing or incomplete.
TIMING_SUPPORTED :: true;
timing_read_ticks :: () -> u64 #expand {
return X64.rdtsc();
}
timing_read_tick_frequency_hz :: () -> u64 {
hz := timing_cpuid_tsc_frequency_hz();
if hz return hz;
timing_check_cpu_id_frequency_information();
return cast(u64) CPU_BASE_FREQUENCY * 1_000_000;
}
// All units in MHz
CPU_BASE_FREQUENCY: u32; // Useful for timing with RDTSC.
CPU_MAX_FREQUENCY: u32; // This is not reliable, will return different values depending on which CPU the thread is called on.
// e.g. on 12900K, we see 5200 on P cores and 3900 on some E-cores.
// In general this isn't useful since all measurements are relative to base frequency.
CPU_BUS_FREQUENCY: u32; // reference bus frequency
CPU_FREQ_RESERVED: u32; // not sure what this is for; most likely unused.
freq_calc_start_time : float64;
freq_calc_start_tick : u64;
// A leaf above the CPU's maximum returns the highest basic leaf's data rather than zeros, so every
// leaf read here is checked against this first. That also makes a vendor check unnecessary: a CPU
// without a leaf reports a lower maximum.
timing_cpuid_max_basic_leaf :: () -> u32 {
max_basic_leaf: u32 = ---;
#asm {
xor.d a: gpr === a, a;
xor.d c: gpr === c, c;
cpuid a, b:, c, d:;
mov.d max_basic_leaf, a;
}
return max_basic_leaf;
}
// Leaf 0x15: TSC Hz = crystal Hz (ECX) * EBX / EAX. 0 when the leaf is missing or leaves any of
// the three out; many CPUs (Skylake client among them) give the ratio but not the crystal.
timing_cpuid_tsc_frequency_hz :: () -> u64 {
if timing_cpuid_max_basic_leaf() < 0x15 return 0;
denominator: u32 = ---;
numerator: u32 = ---;
crystal_hz: u32 = ---;
#asm {
mov.d a: gpr === a, 0x15;
xor.d c: gpr === c, c;
cpuid a, b:, c, d:;
mov.d denominator, a;
mov.d numerator, b;
mov.d crystal_hz, c;
}
if !denominator || !numerator || !crystal_hz return 0;
return cast(u64) crystal_hz * numerator / denominator;
}
// Leaf 0x16 is Intel's (Skylake on). Below it the values stay 0.
timing_check_cpu_id_frequency_information :: () {
if timing_cpuid_max_basic_leaf() < 0x16 return;
base_frequency := *CPU_BASE_FREQUENCY;
max_freq := *CPU_MAX_FREQUENCY;
bus_freq := *CPU_BUS_FREQUENCY;
idk_freq := *CPU_FREQ_RESERVED;
#asm {
mov.d a: gpr === a, 0x16 ; // mov eax, 22 ; 0x16 is the frequency info
xor.d c: gpr === c, c ; // xor ecx, ecx
cpuid a, b:, c, d: ; // cpuid
mov.d [base_frequency], a; // eax
mov.d [max_freq], b; // ebx
mov.d [bus_freq], c; // ecx
mov.d [idk_freq], d; // edx
}
}
X64 :: #import "Machine_X64";