From b2ad4dd189135be9a87af86619d6f5854525f7fa Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Wed, 6 Jul 2022 12:42:01 -0500 Subject: [PATCH 1/2] common/x64: Use TSC clock rate from CPUID when available The current method used to estimate the TSC is fairly accurate - within a few kHz - but the exact value can be extracted from CPUID if available. --- src/common/wall_clock.cpp | 2 +- src/common/x64/cpu_detect.cpp | 13 +++++++++++++ src/common/x64/cpu_detect.h | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index b4fb3a59f..ae07f2811 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -67,7 +67,7 @@ std::unique_ptr CreateBestMatchingClock(u64 emulated_cpu_frequency, const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { - rtsc_frequency = EstimateRDTSCFrequency(); + rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency(); } // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 322aa1f08..4230b2da6 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -161,6 +161,19 @@ static CPUCaps Detect() { caps.invariant_tsc = Common::Bit<8>(cpu_id[3]); } + if (max_std_fn >= 0x15) { + __cpuid(cpu_id, 0x15); + caps.tsc_crystal_ratio_denominator = cpu_id[0]; + caps.tsc_crystal_ratio_numerator = cpu_id[1]; + caps.crystal_frequency = cpu_id[2]; + // Some CPU models might not return a crystal frequency. + // The CPU model can be detected to use the values from turbostat + // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569 + // but it's easier to just estimate the TSC tick rate for these cases. + caps.tsc_frequency = static_cast(caps.crystal_frequency) * + caps.tsc_crystal_ratio_numerator / caps.tsc_crystal_ratio_denominator; + } + if (max_std_fn >= 0x16) { __cpuid(cpu_id, 0x16); caps.base_frequency = cpu_id[0]; diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 9bdc9dbfa..6830f3795 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -30,6 +30,11 @@ struct CPUCaps { u32 max_frequency; u32 bus_frequency; + u32 tsc_crystal_ratio_denominator; + u32 tsc_crystal_ratio_numerator; + u32 crystal_frequency; + u64 tsc_frequency; // Derived from the above three values + bool sse : 1; bool sse2 : 1; bool sse3 : 1; From e71d457af923f373505729fe5a335e6768f6144a Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Wed, 6 Jul 2022 13:00:00 -0500 Subject: [PATCH 2/2] guard against div-by-zero --- src/common/x64/cpu_detect.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index 4230b2da6..1a27532d4 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -170,8 +170,11 @@ static CPUCaps Detect() { // The CPU model can be detected to use the values from turbostat // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569 // but it's easier to just estimate the TSC tick rate for these cases. - caps.tsc_frequency = static_cast(caps.crystal_frequency) * - caps.tsc_crystal_ratio_numerator / caps.tsc_crystal_ratio_denominator; + if (caps.tsc_crystal_ratio_denominator) { + caps.tsc_frequency = static_cast(caps.crystal_frequency) * + caps.tsc_crystal_ratio_numerator / + caps.tsc_crystal_ratio_denominator; + } } if (max_std_fn >= 0x16) {